What is being replicated#
The simulation in one screen: its parts, its runs and its measures. Every number below is read from the release data.
A note on names: the second BLEI reading, called "design-neutral" in the technical record below and in releases up to v5.5, is now called the same-rules reading. Nothing about it changed but the name.
The five components
- CCO, Creative Currency Octaves: every adult who takes part receives a monthly allowance of Basic Units (BU) that buy only essentials and expire; expired BU convert to dollars at higher rates, for work the community values, through the Creative Collectives.
- PTF, Public Trust Foundations: community-owned essential-service providers that accept BU and split what they earn between lower prices, new capacity and their workers.
- PTH, Public Trust Housing: community-owned housing whose payments build the resident’s equity (Acre Equity) instead of paying rent.
- SZH, Social Zone Harmonization: zone coordination of where community businesses and housing go.
- CIP, Citizens Internet Portal: the civic platform that runs the currency and the community’s votes.
How the runs are set up
- 500 simulated adults of working age, living in households as US adults do (single adults, single parents, couples with and without children; Census Bureau figures), with their children, followed for 20 or 40 years, once with Compassionism and once with no program, on the same random draws (common random numbers), so a difference comes from the program and not from luck.
- 500 paired runs (seeds 1 to 500); every figure is their average, with a 95% interval for each change. Each change is the mean of the per-run differences (Compassionism minus no program on the same draws), and its 95% interval is computed on those paired differences, not from two separate averages. The findings explorer also counts, for each headline measure, the runs in which the program does better (“Run by run”).
- Three environments: Reference (the model’s main settings, with no recessions and no outside inflation); Adverse (recessions, 2% outside inflation and an automation wave); Stress Test (the Adverse environment with weaker settings (40% take part, a smaller allowance)).
- The main reading is Compassionism as specified on the Research Hub, every mechanism in, paid for by a Source that issues the BU; cautiously, what the Source pays out is new money except what new output backs. Every other assumption is a labeled reading beside it.
The measures
- Below 30 days of basic living (the Basic Living Economic Index, BLEI): savings, pay and support cover fewer than 30 days of basic costs. The BLEI paper’s definition, with the same-rules reading beside it (earlier called design-neutral: the same rules for everyone: 12% of a month’s wage and the ordinary daily cost of living, plus one month of whatever regular support a design pays; it leaves out the two extras the BLEI paper gives participants (20% of wage, and a lower daily cost in community housing)).
- Below the cost of living: a household’s income short of the MIT living-wage basket for its adults and children; counted for adults, for children and for everyone.
- Too little wealth: net wealth (what someone owns minus what they owe) under $25,000 in today’s money.
- Also: the US official poverty line ($16,749 for one person in 2025), the unhoused share, and the spread of income and wealth (Gini).
- What each Gini estimates: every Gini is the sample Gini of the 500 adults multiplied by n/(n − 1), with n the number of adults. The adults are a random draw from the model’s population, so the corrected figure estimates the Gini of that population (the one compared with the Hub’s national targets) without the small downward bias of a sample. The Gini of the 500 adults themselves, without the correction and slightly lower, is in the release file beside it as a derived figure.
- Poverty lines move with prices every year, so a line means the same standard of living; the findings explorer also shows them fixed in dollars.
Reproduce the figures#
The simulation runs in any browser; checking its figures needs Node.js. The commands below install the one test dependency, run the three checks, run one environment of the release panel, and confirm that the release data file matches the panels. The full recipe for a release, from the 500-run regeneration to the data file and the pages, is dev/ADDING-A-RELEASE.md.
$ git clone https://github.com/BetterToBest/compassionism-simulation $ cd compassionism-simulation $ npm install # jsdom, used only by domtest $ npm test # validate, unit and domtest $ node harness.js testbed 500 release ref # one environment of the release panel $ python3 dev/tools/release_data.py --check # the release data matches the panels
What the tool includes#
What the interactive tool and this package include
- All 5 architectures
- BLEI-calibrated
- Annual expense system
- AI automation
- 50× Monte Carlo 95% CI
- Participant & Social Security-anchored stratification
- Computed OAT sensitivity
- Latin Hypercube sensitivity export
- 6-check validation suite
- JSON export
- Shareable parameter URLs
- ODD protocol
- MC cancel control
- Runs in any browser
- 5,000-seed large-N study per scenario
- BLEI six-tier Flourishing/Comfortable classification
- EDC-adjusted wealth tracking
- Alaska PFD · CLT · Mondragon analogue-calibrated parameters
Current findings#
A five-line summary here; every chart, table and download is on the findings explorer.
The findings in five lines (500 paired runs; Compassionism against no program)
- Reference, 20 years: below the cost of living 19.5% of adult-years with Compassionism against 49.3% with no program; too little wealth 44.4% against 32.7%.
- Below 30 days of basic living (BLEI): 11.7% against 26.5% (Reference), 17.5% against 31.6% (Adverse), 35.3% against 31.6% (Stress Test).
- More adults end with too little wealth than with no program in Reference (44.4% against 32.7%; prices rise 50.8% a year); Adverse (80.9% against 45.1%; prices rise 60.9% a year); Stress Test (76.3% against 45.1%; prices rise 27.4% a year): prices rise with the program’s new money and savings in the model earn nothing.
- Children (Reference, 20 years): below the cost of living 32.2% of child-years with Compassionism against 76.4% with no program; in a household with too little wealth at the last year 65.0% against 52.8%.
- The decisive unknown is how much of the Source’s payout new output backs: if all of it were, too little wealth would be 8.7% (Reference). Cost: $29,874 per adult a year (Reference).
- Against the Hub’s Year 7 target of under 2% poverty (Reference): met on the poverty line on Supplemental-style resources (0.3%), unhoused (0.18%); not met on the official poverty line (8.3%), 30 days of basic living (11.2%), 30 days, same-rules reading (13.4%), the cost of living (19.5%), too little wealth (36.8%).
The tables that used to sit at the top of this page are on the explorer; their old links lead there:
Key concepts and terminology#
Plain-language definitions of every acronym on this page. Wherever a term first appears in a section, hover, focus or tap it for its definition.
Compassionism
The overarching framework developed by Duke Johnson, integrating five coordinated economic and governance architectures into a single system designed to structurally eliminate poverty while preserving market incentives and democratic governance.
Creative Currency Octaves
A dual-currency economic layer. Citizens opt in to receive Basic Units (BU) — a monthly allocation (~$1,200/adult) redeemable at PTF businesses for essential goods. Businesses that accept BU convert them to primary currency at merit-scaled rates (1×–9×), rewarding quality and community contribution.
Basic Units
The community currency issued by the CCO system. Each BU is pegged 1:1 to the primary currency but redeemable at PTF establishments at below-market prices — generating a purchasing power premium (ε) of 2.64× for food and 1.80× for utilities. BU expire monthly, ensuring they circulate toward genuine need.
Public Trust Foundations
Community-owned, democratically governed businesses that accept Basic Units and operate on a not-for-profit model. By eliminating shareholder profit extraction, PTF establishments achieve 40–60% internal overhead reductions (analogous to Mondragon's cooperative model) — a business-economics figure, distinct from the 12–16% consumer-facing cost reduction (PTF_savings_rate in CFG) the simulation itself applies; see Mathematical Framework §2. PTF is modeled as a cost-reduction mechanism for essential goods, not as a wealth-addition channel.
Public Trust Housing
Community-owned residential housing in which monthly payments ($600/month at base case) accrue entirely to the resident's Acre Equity account rather than being lost to market rent or mortgage interest. Rent savings and Acre Equity appreciation are tracked separately to prevent double-counting.
Acre Equity
The cumulative community equity built by PTH residents through their monthly payments, appreciating at approximately 4% per year. Unlike conventional home equity, Acre Equity is accessible at exit only in part — a liquidity haircut whose accessible share rises with tenure, from ~10–20% in the first six months to ~80–90% at five or more years (v4.5; flat 50% regardless of tenure through v4.4).
Social Zone Harmonization
The spatial governance layer that organizes the geographic deployment of PTF businesses and PTH communities within designated zones. SZH governs network density thresholds — the minimum PTF merchant participation needed to activate cooperative synergy effects (θ), which require at least 55% zone participation before activating.
Citizens Internet Portal
A privacy-first digital governance platform that administers CCO currency operations — conversion, distribution, merit assessment — and manages democratic voting for PTH and PTF decisions. CIP provides the transparent institutional layer that prevents elite capture and validates the system's democratic legitimacy.
Basic Living Economic Index
The primary welfare metric in this framework. BLEI expresses how many days of basic living a household's accessible resources cover — a temporal stability measure. Three components: cashDays (liquid savings buffer), incomeDays (income flow buffer), benefitDays (BU food basket). Income buffer uses actual wage, not wealth proxy. See the BLEI Foundation Paper for full accounting convention.
Extractive Drain Coefficient
The fraction of gross income captured by financial contracts that build zero household wealth — rent, mortgage interest, consumer debt interest, predatory fees. PTH payments have EDC = 0.00 (fully generative). PTF participation reduces the cost base directly, lowering effective EDC.
Financial Bandwidth Score
Monthly residual resources above the basic living threshold. Grounded in Mullainathan & Shafir's (2013) scarcity cognition research: without positive FBS, cognitive bandwidth is consumed by immediate survival, making advancement structurally impossible regardless of individual capability. FBS uses EDC_residual (consumer debt only) to avoid double-counting housing costs in C_basic.
Effective Purchasing Power Multiplier
A dimensionless ratio expressing effective purchasing power of a CCO-PTF household relative to a baseline household at the same nominal income. EPPM uses full BU·ε_blended = $2,502 (food + utility BU value) because it measures total purchasing power advantage versus market provision. At the poverty line, EPPM = 5.80×.
Compound Stability Index
A forward-looking projection of BLEI trajectory over time, compounding initial stability by net wealth growth rate adjusted for extractive drain, plus accumulated Acre Equity. W_accessible (which initializes BLEI) excludes AE entirely; r_wealth and r_a evolve independently to prevent double-counting.
Indirect Participant Benefit Index
Estimated annual welfare gains accruing to high-wealth households who do not directly participate in CCO-PTF, estimated at $22,800–$59,900 per year. Channel weights use distinct notation (α, β, π, δ, ζ) to avoid symbol conflict with the BLEI liquidity coefficient γ.
These terms are formally defined and mathematically specified in the BLEI Foundation Paper, including Appendix A (Symbol Table) and Appendix B (Worked Numerical Example). The full Compassionism system architecture is documented in the Executive Summary & Research Index.
Methodology Overview#
This replication framework operationalizes the BLEI welfare measurement suite as a computational agent-based simulation. It is a calibrated institutional simulation architecture — not a conventional econometric study — designed to identify optimal system parameters and test framework behavior across thousands of parameter draws. Key parameters (ε, r_a, BU allocation) are grounded in three deployed real-world analogues: the Alaska Permanent Fund Dividend, Community Land Trusts across 46 US states, and Mondragon Corporation.
The BU purchasing power accounting convention distinguishes food BU contributions to BLEI ($990/adult/month, food excluded from C_basic) from utility BU contributions (offset cash utility cost in C_basic; full value captured in EPPM). The v3.0/3.1 simulation implements an annual expense system where agents pay living costs each year — creating realistic wealth depletion under baseline conditions — and models PTF as a cost-reduction mechanism rather than a direct wealth injection.
Step 1 — BLEI-Initialized Agents with Expense Dynamics
Agents initialized with BLEI, EDC, FBS, and Acre Equity state variables drawn from calibrated US wealth and housing distributions. PTH payments route to Acre Equity from initialization†. v3.1 addition: each agent is assigned a heterogeneous automationRisk ∈ [0.2, 1.0] governing their individual exposure to AI labor displacement. Agents can accumulate debt to a floor of −$10,000 (v3.1), replacing the earlier $0/$100 hard floor that eliminated insolvency dynamics.
† As of v4.0: the initial $5,000 Acre Equity endowment appreciates as before, and a documented 25% share of each year's PTH housing-cost saving now also routes into Acre Equity (the "payments build equity" mechanism this step describes). v4.5: the liquidity haircut on withdrawal is no longer a flat 50% — it now follows the tenure-cohort schedule sketched in the Empirical Calibration notes (≈15% at tenure=1, linearly rising to ≈85% at tenure≥5, the midpoints of the paper's cited ≈10–20%-at-6-months / ≈80–90%-at-5+-years range; the paper gives two anchor points, not a closed-form curve, so the ramp between them is a documented interpretation, not a re-derivation). Flat 50% was used through v4.4.
Step 2 — Extraction-Accurate Dynamics with Annual Costs
Annual wealth updates subtract living costs from wage income and CCO conversion gains. PTF is modeled as a cost-reduction factor (12–16% reduction in annual living costs), not as a wealth-addition channel — correcting the ex nihilo money creation in earlier versions. PTH rent savings and Acre Equity appreciation are tracked in separate accounting categories — removing double-counting. FBS uses EDC_residual (consumer debt only) to avoid double-counting housing costs already in C_basic.
Step 3 — FBS-Gated Advancement
Octave advancement probability is gated by Financial Bandwidth Score. P(advance) = 1 − exp(−λ × FBS). Agents at FBS = 0 cannot advance regardless of quality score. This reflects Mullainathan & Shafir's (2013) cognitive bandwidth research. γ transitions from 0.12 (entry) to 0.20 (CCO-established) as the guaranteed BU floor matures. v3.1: PTF members join dynamically over time via economic distress and social diffusion, not only at initialisation. (v4.4: FBS's wage-derived income term moved onto the same real-dollar anchor the main wealth loop uses, with λ rescaled by the same known ratio to preserve the gate's original sensitivity — see Version History.)
Step 4 — Network-Density Synergy & Inflation
Synergy coefficient θ activates only above 55% PTF merchant participation within a SZH zone, scaling linearly to 0.25 at 90%+ density (the paper's specification). v4.17 correction: the simulation applies this threshold to the SZH zone-coherence slider as a proxy; realized PTF density does not enter θ. v3.1 inflation mechanism: annual costs compound at a configurable rate (0–6%). PTF presence dampens effective inflation (community pricing stability); PTH dampens housing-component inflation (v4.14/v4.15: each damping term now scales with that system's realized population share — previously the initial PTF slider and a flat PTH toggle factor respectively; see Version History). The US Baseline scenario applies 3% annual inflation to reflect historic CPI. Compassionism scenarios default to 0% — the price-stability hypothesis of cooperative essential goods provision. (v4.16 note: as implemented, this hypothesis is an assumed input rather than a model output — at 0% the PTF/PTH damping terms above have nothing to act on, and CCO Only, which has neither PTF nor PTH, also runs at 0% — so every vs-Baseline comparison counts the assumed price stability as part of the policy effect. Measured at N=500: roughly 24–38% of the headline poverty gap. The simulation now offers an opt-in "Match Baseline inflation" toggle; whether matching should be the default is a pending decision for the framework's author. See Version History.)
Step 5 — Recession Model (Population-Level)
v3.1 update: Recessions are modeled as population-level events with 10% annual probability, persisting 1–3 years (corrected v4.1 — previously stated as 2–4 years here, a stale figure that predated the v3.3 distribution change; see v4.1 note above). v3.3 calibration: incomeMultiplier is now drawn from a beta(5,2) distribution over [0.70, 0.95], yielding a mode of 0.90 (10% income loss) with a realistic tail extending to 0.70 (30% loss). This replaces the v3.1 uniform distribution over [0.65, 0.85], which overstated the frequency of severe recessions relative to NBER post-WWII data.
Step 6 — EDC-Adjusted Gini & BLEI Poverty
Gini reported on extraction-adjusted net wealth: W_net = W_nominal − (EDC × Y × 12). This corrects the standard Gini's blindness to the direction of financial flows. v3.1 addition: BLEI poverty (% of agents with BLEI < 30 days = Crisis+Precarious tier) is tracked and displayed as a theoretically grounded alternative to wealth poverty — a retiree with $24K savings and an $80K pension incorrectly registers as wealth-poor but correctly registers as BLEI-stable. BLEI poverty is the primary poverty indicator in v3.3. (v4.4: Y here now resolves via the same real-dollar wage anchor the main wealth loop uses, not the older approximation this formula previously relied on — see Mathematical Framework §5 and Version History.)
BLEI Temporal Stability Tier System#
The six BLEI tiers replace binary poverty/non-poverty classification and gate agent advancement probability in the simulation. Tier assignment derives from BLEI score (days of basic living covered). The Flourishing tier threshold of 730 days reflects the capital commitment horizon required for entrepreneurial ventures to become viable without required break-even within the first year. v3.1 tier naming: the top tier (≥730 days) is labeled Flourishing when both CCO and PTF are active — reflecting the maturing generative effects of the full system. Without CCO+PTF, reaching 730+ days represents strong temporal stability but is labeled Comfortable, consistent with BLEI §4.
| Tier | BLEI Range | Characterization | P(Advance) |
|---|---|---|---|
| 0 — Crisis | < 7 days | Survival-mode cognition; zero advancement capacity; immediate intervention required | ≈ 0 |
| 1 — Precarious | 7–30 days | One disruption from crisis; income fully consumed by extraction and basic needs; Lusardi et al. (2011) $2,000-shock vulnerability zone. BLEI poverty = Crisis + Precarious combined. | ≈ 0.02 |
| 2 — Threshold | 30–120 days | BLEI-defined poverty line; limited advancement feasible; positive FBS emerging | 0.08–0.15 |
| 3 — Stable | 120–365 days | One full year covered; measured risk-taking and education investment viable; Carroll (1997) buffer-stock target zone | 0.20–0.35 |
| 4 — Secure | 365–730 days | One to two years covered; creative and entrepreneurial participation feasible within two-year payoff horizon | 0.40–0.60 |
| 5 — Flourishing (or Comfortable without CCO+PTF) | > 730 days | Two-year capital commitment horizon unlocked; ventures without early-stage break-even are viable; extended planning horizon with reduced liquidity-constrained decision-making | 0.65+ |
Agent Architecture — v3.3 Extraction-Accurate Model#
index.html (github.com/BetterToBest/compassionism-simulation) and CONTRIBUTING.md's Cumulative Bug Fixes for exactly what changed and why.
The v3.1 agent class adds an annual expense system, heterogeneous AI automation risk, negative wealth floor, and PTF dynamic adoption. The corrected agent also separates PTF benefits (cost reduction) from PTH benefits (Acre Equity growth), preventing the double-counting present in earlier versions. The BLEI income buffer uses actual agent wage rather than the wealth/60 proxy. BU_food_eff ($990) is used for BLEI; BU·ε_blended ($2,502) is used for EPPM. v3.3 addition: wage growth stability premium applies diminishing returns above WAGE_MEDIAN_SIU (35 SIU), preventing structural compounding at high wages. The constant ANNUAL_BASE_COST is renamed SIM_COST_SCALE to avoid apparent contradiction with BASE_DAILY_COST.
automation_risk ∈ [0.2, 1.0] drawn uniformly at initialisation — creates heterogeneous inequality under AI displacement (high-risk agents lose wages faster). wealth floor changed from 0 to −$10,000 — allows debt and insolvency dynamics. PTF adoption now dynamic: agents join via economic distress (BLEI < Precarious) or social diffusion at low base rate, not only at initialisation.
Python reference implementation (v3.3 snapshot)
# ═══════════════════════════════════════════════════════════════
# Agent — v3.3 Extraction-Accurate BLEI-Integrated Model
# Key v3.0/3.1 corrections:
# · Annual expense system: agents pay living costs; wealth depletes without CCO
# · PTF: cost-reduction factor, NOT wealth injection (eliminates ex nihilo money)
# · PTH: AE appreciation tracked separately from rent savings (no double-count)
# · BLEI mInc: uses actual wage, not wealth/60 proxy
# · automationRisk: heterogeneous AI displacement per agent [0.2, 1.0]
# · wealth floor: -10,000 (was 0/100 — allows debt dynamics)
# · Recession: incomeMultiplier replaces ambiguous 'severity' field
# · Tier naming: 'Flourishing' (CCO+PTF active) or 'Comfortable' (without)
# v3.3 additions:
# · Wage stability premium: DR factor = 1/(1 + 0.5*max(0, wage/WAGE_MEDIAN_SIU - 1))
# · SIM_COST_SCALE: renamed from ANNUAL_BASE_COST (avoids scale confusion)
# · Gini zero-wealth: totW=0 → Gini=0 (mathematically correct; was ||1 fake fix)
# · NaN/Inf guards throughout agentBLEI, agentEDC, runYear, calcMetrics
# · p10/p90: proper linear interpolation (not index rounding)
# BU accounting: BLEI uses food BU only ($990); EPPM uses full $2,502
# ═══════════════════════════════════════════════════════════════
class Agent:
def __init__(self, agent_id: int):
self.id = agent_id
self.tenure_months = 0
# ── Primary state ──────────────────────────────────────────────
self.wealth = np.random.lognormal(10.5, 1.2) # USD nominal
self.income = np.random.lognormal(3.5, 0.5) # SIU (sim income units, not USD)
# ── Participation flags ────────────────────────────────────────
self.cco_participant = np.random.choice([True, False], p=[0.78, 0.22])
self.pth_resident = np.random.choice([True, False], p=[0.20, 0.80])
self.ptf_member = np.random.choice([True, False], p=[0.18, 0.82])
self.octave_level = 0
self.quality_score = np.random.uniform(1, 9)
# ── v3.1: heterogeneous AI automation risk ─────────────────────
# High risk (→1.0) = occupation highly exposed to automation displacement
# Low risk (→0.2) = occupation resilient (creative, relational, novel)
# Per-agent displacement = population_rate × automation_risk
self.automation_risk = np.random.uniform(0.2, 1.0) # STALE (v3.3) — v4.20 uses a 63/37 bimodal Beta(6,1)/Beta(1,6) mixture (47/53 in v4.3–v4.19), see §3
# ── BLEI state variables ───────────────────────────────────────
self.bu_monthly = 1200.0 if self.cco_participant else 0.0
self.bu_food_eff = 990.0 if self.cco_participant else 0.0
self.bu_eppm = 2502.0 if self.cco_participant else 0.0
self.bu_epsilon_food = 2.64
self.acre_equity = 0.0
self.edc_full = self._calc_edc()
self.edc_residual = self._calc_edc_residual()
self.fbs = 0.0
self.blei_score = 0.0
self.blei_tier = 0
self.lambda_cap = np.random.uniform(0.001, 0.008) # STALE (v3.3) — v4.4 uses U(0.0001654, 0.0013233), see Math. Framework §4
self._update_blei()
self._update_fbs()
def _gamma(self) -> float:
"""γ transitions from 0.12 (entry) to 0.20 (CCO-established, Month 6+)."""
if not self.cco_participant: return 0.12
if self.tenure_months < 3: return 0.12
elif self.tenure_months < 6:
t = (self.tenure_months - 3) / 3.0
return 0.12 + t * (0.20 - 0.12)
return 0.20
def _ae_haircut(self) -> float:
m = self.tenure_months
if m < 7: return 0.15
elif m < 25: return 0.40
elif m < 61: return 0.65
else: return 0.85
def _update_blei(self):
"""
BLEI = cashDays + incomeDays + benefitDays
= [L + γ·Y + BU_food_eff] / C_basic_cash
Three conceptually distinct buffers:
cashDays: liquid / dailyCost (stock buffer)
incomeDays: γ·income / dailyCost (flow buffer; v3.1: actual income, not wealth/60)
benefitDays: bu_food_eff / dailyCost (BU food basket buffer)
"""
gamma = self._gamma()
h_ae = self._ae_haircut() if self.pth_resident else 0.0
liquid = max(0.0, self.wealth) * 0.20 # v3.1: liquid capped at 0 if wealth negative
ae_liq = h_ae * self.acre_equity
income_buf = gamma * self.income # v3.1 fix: actual income (was wealth/60)
if self.cco_participant:
daily_cash = (600 + 150 + 200) / 30
else:
daily_cash = (1300 + 400 + 150 + 200) / 30
self.blei_score = (liquid + ae_liq + income_buf + self.bu_food_eff) / max(daily_cash, 1)
self.blei_tier = self._tier(self.blei_score)
def _tier(self, blei: float) -> int:
if blei < 7: return 0
elif blei < 30: return 1
elif blei < 120: return 2
elif blei < 365: return 3
elif blei < 730: return 4
else: return 5 # Flourishing (CCO+PTF) or Comfortable (without)
def update_wealth(self, cco_income: float, shock: float = 1.0,
acre_rate: float = 0.04, sim_cost_scale: float = 1500.0,
ai_displacement_rate: float = 0.0, inflation_mult: float = 1.0):
"""
Annual wealth update — v3.3 extraction-accurate with expense system.
Key changes from prior versions:
· Annual living costs subtracted (creates realistic wealth pressure)
· PTF modeled as COST REDUCTION FACTOR, not wealth addition
· PTH: AE growth tracked separately from living cost reduction (no double-count)
· Wealth floor: WEALTH_FLOOR (-10,000), not 0 — allows debt dynamics
· AI displacement: per-agent via self.automation_risk (heterogeneous)
· incomeMultiplier replaces ambiguous 'severity' in recession state
· v3.3: wage stability premium applies diminishing returns (Mullainathan & Shafir 2013)
· v3.3: SIM_COST_SCALE replaces ANNUAL_BASE_COST (avoids scale confusion)
"""
self.tenure_months += 1
# Wage dynamics: base growth + stability premium (DR) - AI displacement
wage_growth = 0.010
if self.blei_tier >= 2: # v3.3: stability premium with diminishing returns
dr_factor = 1.0 / (1.0 + 0.5 * max(0, self.income / WAGE_MEDIAN_SIU - 1))
wage_growth += 0.008 * dr_factor # stability premium; M&S 2013 + Carroll 1997
wage_growth += self.octave_level * 0.003 # skill premium
agent_ai_disp = ai_displacement_rate * self.automation_risk # heterogeneous
wage_growth -= agent_ai_disp
self.income = max(self.income * 0.80, self.income * (1 + wage_growth))
# Annual living cost with PTF cost reduction (NOT wealth injection)
# PTH covers housing component (35%); CCO BU covers food (20%)
cost_factor = 1.0
if self.ptf_member:
savings_rate = 0.12 # PTF: 12% cost reduction
cost_factor *= (1 - savings_rate)
if self.pth_resident: cost_factor *= 0.65 # housing covered
if self.cco_participant: cost_factor *= 0.80 # BU food basket
living_cost = sim_cost_scale * inflation_mult * cost_factor
# Net wealth: wage income - living costs + CCO conversion income
self.wealth += self.income * 12 * shock - living_cost + cco_income * shock
# PTH: Acre Equity growth (SEPARATE from cost savings above — no double-count)
if self.pth_resident:
appr = self.acre_equity * (acre_rate / 12)
self.acre_equity += (600 + appr) # payment + appreciation
# v3.1: negative wealth floor allows debt (was max(wealth, 0))
WEALTH_FLOOR = -10000
self.wealth = max(self.wealth, WEALTH_FLOOR)
self._update_blei()
self._update_fbs()
def try_ptf_adoption(self):
"""v3.1: dynamic PTF adoption via distress and social diffusion."""
if self.ptf_member: return
adopt_prob = 0.005
if self.blei_tier < 2: adopt_prob += 0.015 # economic distress
if np.random.random() < adopt_prob: self.ptf_member = True
@property
def blei_poverty(self) -> bool:
"""BLEI poverty = Crisis or Precarious (BLEI < 30 days); primary poverty indicator v3.3."""
return self.blei_tier < 2Simulation Engine — v3.3 Constants#
SIM_COST_SCALE is shown here as the operative driver of the wealth-accumulation ratio (income×12 / SIM_COST_SCALE) — that mechanic was replaced entirely in v4.3 by the WAGE_TO_USD/LIVING_WAGE_ANNUAL formula in Mathematical Framework §2, and the validation assertion below (gini_edc_adjusted < 0.22) reflects a target value that was never reconciled with the shipped code's own CFG.TARGET_GINI = 0.25, let alone the model's actual measured Gini (0.518 under v4.4 — see Performance Comparison). Preserved as historical/illustrative rather than rewritten, for the same reasons given above — see index.html and CONTRIBUTING.md for the current, validated implementation.
The Monte Carlo engine adds the annual expense system, AI automation displacement, negative wealth floor, BLEI poverty tracking, and population-level recession model. The incomeMultiplier field in recession state (renamed from severity) makes the directional meaning unambiguous: 0.90 means agents earn 90% of normal income (10% loss) at the distribution's mode. v3.3: recession severity now drawn from a beta(5,2) distribution over [0.70, 0.95] for NBER-calibrated realism; SIM_COST_SCALE renamed from ANNUAL_BASE_COST; WAGE_MEDIAN_SIU added as diminishing returns reference.
Simulation constants (v3.3 snapshot)
# ═══════════════════════════════════════════════════════════════
# Simulation Constants — v3.3
# v3.1: ANNUAL_BASE_COST, WEALTH_FLOOR, AI displacement schedule
# v3.1: incomeMultiplier replaces severity; BLEI poverty tracked
# v3.3: SIM_COST_SCALE (renamed from ANNUAL_BASE_COST)
# v3.3: WAGE_MEDIAN_SIU for diminishing returns
# v3.3: RECESSION_BETA_A/B for beta(5,2) distribution over [0.70, 0.95]
# ═══════════════════════════════════════════════════════════════
# BU accounting (unchanged)
BU_MONTHLY_ADULT = 1200
BU_FOOD_EFF = 990 # BLEI numerator (food BU only)
BU_EPPM = 2502 # EPPM full blended value
BU_EPSILON_FOOD = 2.64
BU_EPSILON_UTIL = 1.80
# PTH
PTH_PAYMENT = 600 # 100% routes to Acre Equity (EDC = 0)
ACRE_APPRECIATION = 0.04 # annual r_a; monthly: r_a/12
# v3.3: renamed from ANNUAL_BASE_COST to avoid apparent contradiction with BASE_DAILY_COST
# ($68.33/day ≈ $24,941/yr vs 1,500 SIU sim scale — different unit systems)
# Operative ratio: income*12 / SIM_COST_SCALE
# Baseline only: ratio < 1 → wealth depletion (creates realistic poverty)
# Full CCO+PTF+PTH: ratio > 1 → wealth accumulation
# PTF applied as cost reduction factor; NOT added to wealth
SIM_COST_SCALE = 1500 # calibrated simulation units; renamed from ANNUAL_BASE_COST
# v3.3: reference wage for diminishing returns on stability premium
WAGE_MEDIAN_SIU = 35 # median SIU wage; DR formula: 1/(1 + 0.5*max(0, wage/35 - 1))
# v3.1: negative wealth floor — allows debt/insolvency dynamics
WEALTH_FLOOR = -10000 # was 0/100 in prior versions
# v3.1: AI automation displacement schedule (Duke's labor market notes)
# Labor markets cannot reabsorb past 30% displacement (~yr 5, ≈2030)
# Demand contracts at 50% displacement (~yr 15, ≈2040)
# Per-agent displacement = population_rate × agent.automation_risk
AI_DISPLACEMENT_YEAR_1 = 5
AI_DISPLACEMENT_YEAR_2 = 15
AI_DISPLACEMENT_RATE_1 = 0.012 # annual wage drag, phase 1
AI_DISPLACEMENT_RATE_2 = 0.022 # annual wage drag, phase 2
# Synergy
SYNERGY_THETA_MIN = 0.05
SYNERGY_THETA_MAX = 0.25
NETWORK_THRESHOLD = 0.55
# BLEI tier thresholds
BLEI_CRISIS = 7
BLEI_PRECARIOUS = 30 # BLEI poverty = Crisis + Precarious (< 30 days)
BLEI_THRESHOLD = 120
BLEI_STABLE = 365
BLEI_SECURE = 730 # Flourishing/Comfortable threshold
# v3.3: beta distribution parameters for recession severity [v4.0: mode figure corrected]
# beta(5,2) over [0,1] → mode at (5-1)/(5+2-2)=0.80; scaled to [0.70, 0.95] → mode = 0.90
# 0.90 incomeMultiplier = 90% of normal income = 10% loss (mean/median ≈0.88, ~12% loss)
# Tail extends to 0.70 (30% loss) — calibrated against NBER post-WWII data
# Prior (v3.1): Uniform(0.65, 0.85) overstated frequency of severe recessions
RECESSION_BETA_A = 5 # beta(5,2) shape parameter a
RECESSION_BETA_B = 2 # beta(5,2) shape parameter b; incomeMultiplier = 0.70 + beta*0.25
SIMULATION_RUNS = 10000
YEARS = 20
# v3.3: Recession state uses incomeMultiplier with beta(5,2) distribution
# v3.1 renamed from 'severity' — 0.90 means 90% of normal income (10% loss) at the mode
# v3.3 range: [0.70, 0.95]; v3.1 range was [0.65, 0.85] (uniform)
class RecessionState:
active: bool = False
years_left: int = 0
income_multiplier: float = 1.0 # v3.1: renamed from 'severity'; v3.3: beta(5,2) drawn
def update_recession(state: RecessionState) -> RecessionState:
if state.active:
state.years_left -= 1
if state.years_left <= 0:
state.active = False; state.income_multiplier = 1.0
elif np.random.random() < 0.10:
state.active = True
state.years_left = np.random.randint(2, 5) # 2-4 year recession
# v3.3: beta(5,2) over [0.70, 0.95]; mode = 0.90 (NBER post-WWII calibration)
state.income_multiplier = 0.70 + np.random.beta(RECESSION_BETA_A, RECESSION_BETA_B) * 0.25
return state
class MonteCarloAnalysis:
def __init__(self, n=SIMULATION_RUNS):
self.n = n
def run(self):
results = []
for _ in range(self.n):
params = {
'bu_monthly': np.random.uniform(1000, 1500),
'pth_uptake': np.random.uniform(0.15, 0.25),
'ptf_share': np.random.uniform(0.12, 0.25),
'network_density': np.random.uniform(0.40, 0.95),
'participation': np.random.uniform(0.60, 0.95),
'bu_epsilon_food': np.random.uniform(1.50, 3.00),
'acre_rate': np.random.uniform(0.02, 0.07),
'octave_max': np.random.randint(4, 9),
'quality_accuracy': np.random.uniform(0.70, 0.95),
'sim_cost_scale': np.random.uniform(1200, 1800), # v3.3: renamed from annual_base_cost
'inflation_rate': np.random.uniform(0.00, 0.04), # v3.1
}
theta = synergy_theta(params['network_density'])
results.append(self._run_scenario(params, theta))
return self._aggregate(results)
def validate(self, r: dict):
"""Internal calibration benchmarks — design targets, not external validation."""
assert r['blei_threshold_rate'][1] > 0.95
assert r['blei_secure_rate'][1] > 0.80
assert r['gini_edc_adjusted'][1] < 0.22 # STALE target — CFG.TARGET_GINI=0.25 in shipped code; measured v4.4 output is 0.518
assert r['median_eppm'][1] > 2.00
assert r['system_stability'] > 0.90
assert r['blei_poverty_rate'][1] < 0.05 # <5% Crisis+Precarious
# v3.3: participant stratification and validation suite
assert r['participant_poverty_rate'] <= r['non_participant_poverty_rate'] # CCO benefit direction
assert all(v == 'PASS' for v in r['validation_suite'].values()) # 4 consistency checks
print("All internal calibration benchmarks passed.")Calibrated Parameters#
Parameters identified across 10,000+ Monte Carlo simulations as producing strong welfare outcomes. Note on calibration status: values marked under revision were produced under less rigorous simulation versions and are being updated as v3.x matures. An open goal of this work is identifying parameter configurations that deliver best welfare outcomes across all points simultaneously — analogous to a structural engineer ensuring load capacity.
WAGE_TO_USD/LIVING_WAGE_ANNUAL instead (see Mathematical Framework §2) — that switch happened in v4.3. The constant survives in the codebase only as a historical value; it is not load-bearing anywhere in the current simulation. supersededincomeMultiplier now drawn from beta(5,2) distribution over [0.70, 0.95]: mode 0.90 (10% income loss), tail to 0.70 (30% loss). Calibrated against NBER post-WWII data. Replaces v3.1 Uniform(0.65, 0.85), which overstated severe recession frequency.Performance Comparison#
v4.21 note — current figures, confirmed at 5,000 agents per run. The table below is the v4.4 study (5,000 seeds of 500 agents). v4.21 reran the headline at 500 seeds of 5,000 agents on the current engine (node harness.js largen 500 headline --agents=5000): Full Integration wealth poverty 15.26% ±0.05, BLEI poverty 12.48% ±0.04, net basket poverty 9.93% ±0.04, median wealth $527,328 ±900, median BLEI 1,827d ±3, Gini 0.519; Baseline at 3% 71.6% / 70.6%; Baseline matched at 0% 50.4% / 49.0%. Reductions in wealth / BLEI poverty: 78.7% / 82.3% against the shipped Baseline, 69.7% / 74.5% against the matched one, 56.5% / 61.1% with both at 3%, and 59.6% for wealth poverty against year 0. Each agrees with the 500-agent figure within its interval, and Full Integration's poverty rates and median wealth do not change with population size from 250 to 20,000 agents. v4.21's relief fix moves only runs with inflation on (Adverse Environment 40.3% wealth poverty, Stress Test 62.0%); none of the three scenarios below is one of them, except the Baseline, which has no CCO.
v4.20 note: the three scenarios below all run with AI automation off, so v4.20's automation-risk recalibration does not reach them. Its sampler change gives them a different random realization, not a different distribution: over 500 seeds Full Integration's wealth poverty is 15.30% under v4.19 and 15.27% under v4.20, and median wealth $526,629 against $528,624. The large-N figures below therefore stand. Scenarios with automation on (High Automation, Adverse Environment, Stress Test) did move; see Version History, v4.20, and CHANGELOG.md's v4.20 Release Notes.
Results from 5,000 independent seeded runs each of Full Integration, CCO-Only, and Traditional Welfare Baseline (v4.0 large-N study, reconfirmed unchanged under v4.1, reconfirmed again under v4.2's population-pairing mechanics, superseded by v4.3's mechanics-changing main-wealth-loop unit fix and re-run again under v4.4's further mechanics changes — seeds 1–5,000, 500 agents, 20 years, shock disabled to match the Full Integration reference preset; methodology below). BLEI-based metrics are the primary welfare standard; BLEI poverty (% Crisis+Precarious) is the primary poverty indicator. These are internally calibrated simulation outcomes under the reference parameter configuration — they represent a design reference point, not a prediction of outcomes under any specific deployment scenario. 95% CI below is on the mean of each statistic across the 5,000 runs (not per-agent spread within one run) — narrow CIs here reflect a large, well-powered sample, not low real-world variance.
The v4.4 large-N study at a glance
These tiles headed this page through v4.21. They are the v4.4 study (August 2026, 5,000 runs per scenario); the current figures are in the v4.21 note above and at the top of the page.
(≥ 30 days, N=5,000, v4.4)
(≥ 365 days, N=5,000, v4.4)
(full deployment, N=5,000, v4.4)
(20yr, N=5,000, v4.4; see note — target predates v4.3's unit fix; v4.5's PTH haircut change moves this up a further ~0.6%, not yet re-run at N=5,000, see Version History)
(structural metric; N=5,000, v4.4; see note)
(N=5,000, v4.4; was ~13.2 yr pre-v4.3; see note)
(N=5,000, v4.4; was NOT reached even by yr 40 pre-v4.3; see note)
Performance metrics, v4.4 large-N study (5,000 runs per scenario)
Performance Metrics (v4.4 large-N study, Aug 2026: 5,000 seeded runs,
seeds 1–5,000, 500 agents, 20 years each; Full Integration reference
configuration; superseded the v4.0/v4.1/v4.2 figures below via v4.3's
main-wealth-loop unit fix, then again via v4.4's wage-scale unification
across BLEI/FBS/Gini + true CRN pairing — see Version History for what
changed at each step):
CCO-PTF Integrated System (full deployment, reference parameters):
├── BLEI Threshold Rate (≥ 30 days): 87.5% [95% CI 87.4–87.5%]
├── BLEI Stable Rate (≥ 120 days): 84.1% [95% CI 84.1–84.2%]
├── BLEI Secure Rate (≥ 365 days): 78.6% [95% CI 78.5–78.6%]
├── BLEI Flourishing Rate (≥ 730 days): 71.6% [95% CI 71.5–71.6%]
├── BLEI Poverty Rate (Crisis+Precarious): 12.5% [95% CI 12.5–12.6%; target <5%, see note]
├── Median BLEI, final year: 1,824 days [95% CI 1,820–1,827]
├── Median Wealth, year 10: $191,754 [95% CI $191,339–$192,169]
├── Median Wealth, year 20: $526,120 [95% CI $525,196–$527,044; target $82,000 — see note, this target predates v4.3's wage-loop fix and is now far off-scale]
├── Gini (EDC-adjusted net wealth): 0.5183 [95% CI 0.5179–0.5187; target ≤0.25, see note]
├── Gini (nominal wealth, reference only): 0.5100 [95% CI 0.5096–0.5104]
├── Median FBS, CCO participants: not recomputed this pass (tracked open — v4.0's figure, $744/mo, predates the v4.3/v4.4 wage-scale changes and should not be read as current)
├── Participant Poverty Rate: 10.0%
├── Non-Participant Poverty Rate: 34.3%
├── System Stability (structural metric — see Known Limitations for a v4.4 disclosure on how to read this number): 88.6% [95% CI 88.6–88.6%]
├── EPPM at poverty line: not computed by the simulation engine (paper-level calculation only — see BLEI paper §6)
├── Work Incentive Preservation: not computed by the simulation engine
└── Monte Carlo CI (n≥10, in-app): Mean ± t·SD/√n, Student's t with df=n−1 (v4.15; a fixed 1.96 through v4.14) [10×/50× "Stochastic CI" buttons]
Near-poverty cohort tracking (agents with year-0 wealth < $25,000 poverty line;
37.8% of the population under the reference wealth distribution — consistent
with prior versions, this tag is about initial wealth, not wage):
├── Reaches BLEI Threshold (≥30d), cohort median: immediate (month 0, pre-dynamics),
│ in 5,000 of 5,000 runs — from initial liquid wealth alone, not simulated
│ improvement. Unaffected by v4.3/v4.4 — an initialization-state fact, not
│ touched by either release's wage-scale or wealth-loop mechanics.
├── Reaches BLEI Stable (≥120d), cohort median: 36.0 months / 3.0 years (mean 38.6mo)
│ — dramatically faster than the pre-v4.3 figure (158.8 months / 13.2 years).
│ This is a real, large, mechanistically-understood shift, not drift: this
│ cohort is wealth-poor but not wage-poor (most members have ordinary wage
│ draws), and v4.3's wealth-loop fix rewards wage level specifically — see
│ Version History, v4.3 entry.
├── Reaches BLEI Secure (≥365d), cohort median: 108.0 months / 9.0 years (mean 104.9mo)
│ — new milestone, first tracked at this granularity in v4.3.
└── Reaches BLEI Flourishing (≥730d), cohort median: 156.0 months / 13.0 years (mean
161.0mo), reached in 5,000 of 5,000 runs within the 20-year horizon — a complete
reversal from the pre-v4.3 figure (NOT reached, 0 of 5,000 runs, even after
extending to 40 years under pre-v4.3 mechanics). This is the single largest
change any release has produced in this table. See Version History, v4.3 entry,
for the mechanism and why it is a genuinely positive, not merely numerical, shift.
Social-Security-anchored income cohorts (new in v4.3; N=5,000, Full Integration vs.
Traditional Welfare Baseline, same seeds/methodology as above. Unlike the near-poverty
cohort, these are tagged by initial WAGE against real 2026 SSA benefit levels via
WAGE_TO_USD, not by initial wealth — the population most relevant to a genuine
poverty-elimination claim, not median-wage workers. Figures below are v4.4; the
original v4.3 figures are superseded — see Version History for both):
├── SSI-level (≈0.8% of pop., $994/mo → 9.89 SIU via WAGE_TO_USD):
│ Full Integration — wealth poverty 96.8%, BLEI poverty 82.7%, median-of-medians
│ BLEI 19.8d [mean 26.7d — see note on why median, not mean, below], Flourishing 1.4%.
│ Baseline — wealth poverty 99.8%, BLEI poverty 99.8%, median BLEI 1.9d.
│ Cohort medians cross the 30-day Threshold in only 10.9% of seeds (533/4,888
│ seeds that drew ≥1 qualifying agent — this cohort is small, ≈4 agents per
│ 500-agent run, so per-seed medians are noisy; directional, not precision-grade).
├── SSDI-level (≈7.7% of pop., $1,630/mo → 16.22 SIU):
│ Full Integration — wealth poverty 83.0%, BLEI poverty 75.4%, median-of-medians
│ BLEI 22.6d [mean 22.6d — this cohort's distribution is not notably skewed],
│ Flourishing 8.1%. Baseline — wealth poverty 99.7%, BLEI poverty 99.7%, median
│ BLEI 3.0d. Cohort medians cross Threshold in 0.0% of seeds (2/5,000).
└── Retirement-level (≈17.1% of pop., $2,071/mo → 20.60 SIU):
Full Integration — wealth poverty 64.7%, BLEI poverty 52.7%, median-of-medians
BLEI 25.1d [mean 32.0d], Flourishing 14.5%. Baseline — wealth poverty 99.6%,
BLEI poverty 99.6%, median BLEI 3.7d. Cohort medians cross Threshold in 31.1%
of seeds (1,554/5,000) — the closest of the three anchors, but the TYPICAL
(median) outcome still falls short.
A note on median vs. mean for these cohorts: BLEI is right-skewed (bounded below
at 0, long right tail), and these cohorts are small (≈4–86 agents per 500-agent
run) — the MEAN of per-seed cohort-medians can be pulled well above the typical
outcome by a small number of high-BLEI seeds. The retirement-level cohort's mean
(32.0d) sits above the 30-day Threshold; its median-of-medians (25.1d) does not,
and only 31.1% of individual seeds actually cross the line. Reporting the mean
alone here would overstate this cohort's typical outcome — median-of-medians is
the fairer summary, alongside the seed-crossing-rate for full transparency.
Read plainly: Full Integration helps this population enormously in relative
terms (originally measured at 20–30× baseline's BLEI under v4.3 mechanics;
recomputed at ~7–10× under v4.4 — baseline's own BLEI also rose under the
wage-scale fix, since baseline agents receive the same corrected wage-buffer
term with no CCO/PTF/PTH mechanism to offset it) but does not come close to
lifting it out of poverty in absolute terms by the simulation's own tier
definitions, in the typical case, for any of the three anchors. This remains
the sharpest test of the framework's poverty-elimination claim run so far.
v4.6 rebuild — same cohort, extended to a 40-year horizon under CURRENT mechanics
(N=1,000 seeds, a fresh draw, Full Integration; seeds 1–1,000 — same methodology as
the v4.1 extension it replaces, which was three mechanics-changing releases stale
by the time this was run). The milestone question the v4.1 extension originally
asked is now moot: v4.3's wealth-loop fix already answers it inside the existing
20-year table above (median 13.0 years to Flourishing, 5,000/5,000 runs), and this
40-year rebuild reconfirms that almost exactly at an independent N=1,000:
├── Threshold (≥30d), Stable (≥120d), Secure (≥365d), and Flourishing (≥730d)
│ milestones all reconfirm the 20-year table above closely: immediate/month 0
│ (1,000/1,000), 36.0 months [mean 39.0mo] (vs. the 20yr table's 36.0mo/mean
│ 38.6mo), 105.2 months [8.8yr] (vs. 104.9 months/8.7yr), and 156.0 months
│ [13.0yr, mean 161.2mo] (vs. 156.0 months/13.0yr, mean 161.0mo) — reached in
│ 1,000/1,000 runs. This close a match across an independent seed range at a
│ 5× smaller N is itself informative: the 20-year figures are not an artifact
│ of the specific 5,000-seed draw, and extending the horizon doesn't change
│ WHEN the cohort crosses milestones it already clears well inside 20 years.
├── What extending to 40 years actually adds: NO plateau. Cohort median BLEI by
│ year: 201d (yr 5) → 488d (yr 10) → 951d (yr 15) → 1,618d (yr 20) → 2,522d
│ (yr 25) → 3,706d (yr 30) → 5,190d (yr 35) → 7,058d (yr 40) — more than
│ quadrupling between year 20 and year 40 alone, a sharp reversal from the
│ pre-v4.3 extension this replaces, whose cohort median climbed only to ~428d
│ by year 40 (see the archived v4.1 figures, preserved below for comparison).
└── Year-40 population-wide headline (same N=1,000 runs, whole population not just
the cohort): median wealth ≈$2,115,547 (mean $2,117,807) — more than 4× the
year-20 figure — EDC-adjusted Gini 0.422 (down from 0.518 at year 20, further
equalization, not a reversal), wealth poverty 3.8%, BLEI poverty 3.2%,
Flourishing rate 91.8%, population median BLEI ≈7,252 days. This is a
genuinely new finding, not a repeat of v4.3's already-documented
redistribution story: the main wealth-accumulation loop has no explicit
long-run ceiling — no retirement, no consumption floor that scales with
wealth, no diminishing-returns term beyond the existing wage-growth
dampening — so once an agent's annual surplus consistently exceeds cost,
wealth compounds without an apparent bound over multi-decade horizons. A
median of $2.1M per adult household 40 years out is well outside any
real-world reference point this document uses elsewhere (Fed SCF, Census/
BLS) and should not be read as a projection. Tracked as a new open
question in Known Limitations and CONTRIBUTING.md's Model Architecture
Feedback, not resolved here — see CHANGELOG.md's v4.6 Release Notes for
the full methodology and figures.
(Archived for comparison — the pre-v4.3 v4.1 extension this rebuild replaces,
N=1,000, seeds 1–1,000, Full Integration, run under mechanics three
mechanics-changing releases stale by the time it was superseded: Threshold and
Stable milestones matched the pre-v4.3 20-year table (median 12 months and 165
months respectively); Flourishing was NOT reached by year 40 in any of 1,000 runs,
with cohort median BLEI climbing only to ~428d by year 40 — still under 60% of the
way to the 730-day line after doubling the horizon. The gap between that result and
the v4.6 rebuild above is entirely attributable to v4.3/v4.4/v4.5's wealth-loop and
wage-scale mechanics changes, not to any change in this extension's own
methodology.)
PTF mechanism (v3.0 corrected):
PTF modeled as cost reduction (12-16%), NOT wealth injection.
Earlier versions incorrectly added PTF savings to wealth ex nihilo.
PTH mechanism (v3.0 corrected; equity-contribution added v4.0):
Acre Equity appreciation tracked separately from housing cost reduction.
v4.0 adds a 25% share of the annual housing-cost saving routing to Acre Equity
(see Mathematical Framework and Known Limitations for the flat-vs-tenure-cohort
haircut caveat).
Tier naming (v3.1):
CCO+PTF active → top tier = Flourishing (generative system maturation)
Without CCO+PTF → top tier = Comfortable (strong stability, not full Compassionism)
Calibration notes (v4.0 finding; substantially changed under v4.3/v4.4):
Median Wealth (year 20) landed within 0.5% of the pre-existing $82,000 design
target under v4.0-v4.2 mechanics — that close match no longer holds. v4.3's
main-wealth-loop unit fix (replacing a bare-subsistence cost anchor, wrong for
a 20-year compounding loop netted against real wage, with a genuine living-wage
anchor) moved median wealth to $495,411; v4.4 moved it further to $526,120 — more
than 6× the original target. This is a deliberate, documented consequence of a
unit-correctness fix, not drift, and TARGET_WEALTH itself has not been
reconsidered against the new mechanics (a framework-level decision, flagged as
open in CONTRIBUTING.md and, as of v4.4, disclosed directly on the interactive
tool's KPI badges — see Known Limitations). Reading "close to target" or "far from
target" against the old $82,000 figure is no longer meaningful either way. BLEI
Poverty Rate (12.5%) and EDC-adjusted Gini (0.518) both still fall short of their
own pre-existing design targets (<5% and ≤0.25 respectively), though both moved
toward the target, not away from it, under v4.3/v4.4 — see "On calibration versus
validation" below and the Internal Calibration Benchmarks section for what this
does and doesn't imply.
CCO-Only System (no PTH/PTF/SZH/CIP; N=5,000, same seed range; partRate 78%,
matching the Full Integration reference preset — see the participation-rate note
in Methodology Overview):
├── BLEI Threshold Rate: 79.0%
├── BLEI Poverty Rate: 21.0%
├── BLEI Tier at 730+ days: Comfortable (CCO active; PTF not)
├── Median Wealth, year 20: $391,742 [95% CI $390,856–$392,627]
├── Gini (EDC-adjusted): 0.5763 [95% CI 0.5759–0.5768]
└── System Stability (structural metric): 88.4%
Traditional Welfare Baseline (US + 3% CPI, no CCO/PTF/PTH/SZH/CIP; N=5,000, same seed range):
├── v4.16 note: these rows run at 3% CPI; the Full Integration and CCO-Only rows above run
│ at 0%. Inflation-matched at 0% (N=500, harness.js infl-match): wealth poverty 50.5%,
│ BLEI poverty 49.1%, median pinned at WEALTH_FLOOR in 0.6% of runs, not 100% —
│ see CHANGELOG.md v4.16 Release Notes.
├── BLEI Threshold Rate: 29.4%
├── BLEI Poverty Rate (Crisis+Precarious): 70.6%
├── Median Wealth, year 20: −$10,000 — pinned exactly at WEALTH_FLOOR
│ in 5,000 of 5,000 runs (100%). v4.3's living-wage cost anchor roughly doubled
│ the pre-v4.3 cost figure baseline is measured against, without any offsetting
│ mechanism; the floor is now binding for the entire population's median, not an
│ occasional insolvency case as originally intended — see Known Limitations and
│ CONTRIBUTING.md's Model Architecture Feedback. Not resolved by v4.4's removal
│ of a separate, unrelated ×0.85 initial-wealth haircut (Version History,
│ v4.4) — the binding constraint is the ongoing annual gap, not the starting point.
├── Gini (EDC-adjusted): 0.8580
├── System Stability (structural metric): 99.0% — read this alongside the poverty
│ figures above, not in isolation; see Known Limitations for why a population
│ pinned at a hard floor scores as more "stable" than a thriving one under this
│ metric's own definition.
└── Note: wage income < annual living costs at this inflation rate; wealth depletes
over time by design (see Mathematical Framework §2)
Nordic Benchmark (external reference, not simulation output):
├── Gini (post-redistribution): 0.27–0.29
└── CCO-PTF EDC-adjusted Gini (0.518, v4.4 measured) exceeds the Nordic range —
see "On calibration versus validation" below.incomeMultiplier (renamed from ambiguous "severity"); (4) negative wealth floor −$10,000 allowing debt/insolvency; (5) PTF dynamic adoption via distress and diffusion; (6) BLEI poverty KPI (% Crisis+Precarious) alongside wealth poverty.
makeAgent()/runYear()/calcMetrics() exactly as the browser does, for 5,000 independent runs per scenario (15,000 total), seeds 1–5,000, at the Full Integration reference population size (500 agents) and horizon (20 years). Each run's final-year tier distribution, Gini (nominal and EDC-adjusted), median wealth (years 10 and 20), median FBS among CCO participants, participant/non-participant poverty, and the System Stability heuristic were recorded, then aggregated (mean, median, SD, 95% CI on the mean) across runs. The near-poverty cohort is tagged once per run at initialization (year-0 wealth < $25,000) and tracked longitudinally for the milestone-crossing figures. This is a documented, reproducible methodology superseding the previously-unspecified "10,000 simulation runs" claim (batch size, seed range, and code path were never recorded for that figure) — script available on request via a GitHub issue.
makeLatentPopulation(), instantiateAgent(), runYear(), calcMetrics(), bleiMetrics(), structuralStability() called directly, seeds 1–5,000, 500 agents, 20 years, shock disabled). Two additions to the methodology, both new in v4.3 and carried through to this v4.4 re-run: the Social-Security-anchored cohort tagging (agents' initial, pre-dynamics wage compared against real 2026 SSA benefit levels via WAGE_TO_USD — SSI, SSDI, and average retired-worker levels — the same convention the near-poverty cohort already used for initial wealth), and reporting BLEI figures for small cohorts (≈4–86 agents per run) as median-of-medians across seeds rather than mean, since BLEI is right-skewed and a small cohort's mean is more exposed to a handful of high-BLEI seeds — a distinction that mattered directly for this release: an initial mean-based reading of the retirement-level cohort would have overstated its typical outcome as crossing the 30-day Threshold, when the median-of-medians and the per-seed crossing rate (31.1%) show the typical case still falls short. Two supplementary figures in the table above (year-10 median wealth, nominal-wealth Gini) required a small additional pass beyond the core headline metrics, run separately against the same seed range; median FBS among CCO participants was not recomputed this pass and is flagged accordingly in the table, since the pre-v4.3 figure predates the wage-scale changes and would be misleading if left uncaptioned.
Sensitivity Analysis#
156 parameter sensitivity tests across all key variables. The most important finding: BLEI outcomes remain above Tier 2 Threshold for CCO-PTF participants across all tested parameter combinations at Month 12. Month 0 values use γ = 0.12 (entry) and food BU effective value ($990). Note on sensitivity methodology: the elasticity values below are scenario-comparison estimates from the Monte Carlo analysis — not formal Sobol indices or Latin hypercube results. Formal sensitivity analysis using these methods is a documented future research direction in CONTRIBUTING.md. v3.3 update: the interactive simulation's OAT sensitivity chart now reports results from 11 computed mini-simulations (±20% on 5 parameters, seed 7777), replacing the prior hardcoded illustrative guidance table; these are first-order estimates only and do not capture interaction effects. v3.4 framing note: the OAT sensitivity shown is scenario-comparison from actual mini-runs (±20%, seed 7777), not computed Sobol or Latin hypercube indices. This correctly flags first-order parameter effects but does not quantify interactions. v4.0: the ±20% perturbation is now uniformly defined as ±20% of each parameter's own slider range across all five parameters (previously a mix of relative and fixed-percentage-point steps — see the Simulation page's Cumulative Bug Fixes). For formal Sobol analysis, use the simulation's CSV export with external Python/R.
Parameter elasticity testing (156 variables)
Parameter Elasticity Testing (156 variables; BLEI Threshold Rate as primary outcome):
High-Impact Parameters:
├── Basic Unit Amount: Elasticity = −2.34 (poverty rate response)
│ $800/mo → BLEI Threshold Rate: 91%; $1,500/mo → 99%
├── PTF Investment Level: Elasticity = 1.67 (wealth accumulation)
├── Simulation Cost Scale: Elasticity = 1.45 (poverty rate; v3.0 addition)
│ Higher sim cost → faster baseline wealth depletion → more poverty contrast
├── Participation Rate: Elasticity = 1.15 (system performance)
│ Minimum viable threshold: 55–60% participation
└── Quality Assessment Accuracy: Elasticity = 0.94 (innovation output)
Moderate-Impact Parameters:
├── ε_food (BU food purchasing power): Elasticity = 0.58 (BLEI at Month 0)
│ All ε values maintain Tier 2+ at Month 12
├── AI Automation Risk (avg): Elasticity = 0.52 (wage trajectory; v3.1)
│ High-risk agents experience 3–5× more wage erosion than low-risk agents
├── Inflation Rate: Elasticity = 0.48 (cost pressure; v3.1)
│ 3% annual inflation → 81% cost increase over 20 years without PTF/PTH offset
├── Acre Appreciation Rate r_a: Elasticity = 0.45 (long-run wealth)
└── Network Density: Elasticity = 0.38 (synergy activation)
Robust Parameters (low sensitivity):
├── Octave Multiplier: Elasticity = 0.21
├── Conversion Tax Rate: Elasticity = 0.18
├── Phi Rate (Φ): Elasticity = 0.12 (consistent benefit)
└── PTF Dynamic Adoption Rate: Elasticity = 0.08 (emergent, not assumed)
Stability Thresholds:
├── BU Amount: $800–$1,500 (reference $1,200)
├── PTH Uptake: 15%–25% (reference 20%)
├── PTF Share: 12%–25% (reference ~18%; under revision as v3.x matures)
└── Participation: 55%–95% (papers' minimum-viable 55%; not a dynamic in the simulation — v4.17)Mathematical Framework#
1. BLEI Temporal Stability Index — Three-Component Structure
v4.4 note (supersedes the v4.0 note this paragraph previously carried): the simulation's internal wage state is tracked in Simulation Income Units (SIU), a separate calibration unit from the USD figures L, BU_food_eff, and C_basic use — so Y here is WAGE_TO_USD-converted before entering this formula. Through v4.3, this formula (and §4 FBS, §5 EDC-adjusted Gini) used a different, cost-side-derived constant, SIU_TO_USD (≈16.63), while the wealth-accumulation loop in §2 already used WAGE_TO_USD (≈100.52, Census/BLS-sourced) — leaving the same agent's wage worth roughly $582/month here versus roughly $3,518/month in §2, a ~6× divergence external audit correctly flagged as indefensible once one anchor is empirically grounded and the other isn't. SIU_TO_USD is retired as of v4.4; every formula in this section — §1 (this one), §2, §4, and §5 — now uses the same WAGE_TO_USD anchor throughout. agentEDC() was and remains unaffected by any of this — it's a dimensionless SIU/SIU ratio that never touched either constant.
2. Wealth Dynamics with Annual Expense System
v4.3/v4.4 note (supersedes the v4.0 note this paragraph previously carried, which described an unresolved sensitivity problem — now resolved): through v4.2, Y and living cost here were intentionally left in native internal SIU/SIM_COST_SCALE terms rather than converted to USD, because an early tested USD-converting version was extremely sensitive to the conversion constant (a ~3.5× range of candidate values swung 20-year outcomes from universal wealth-floor collapse to implausible multi-million-dollar median wealth) — flagged then as a candidate research direction, not a solved problem. v4.3 resolved this: WAGE_TO_USD (≈100.52, Census/BLS CPS ASEC 2023 median personal income ÷ 35 SIU × 12) converts wage, and LIVING_WAGE_ANNUAL ($49,370, World Population Review's MIT-sourced state living-wage table) anchors cost — replacing the old BASE_DAILY_COST-netted-against-real-wage approach, which was a bare-subsistence floor correct for BLEI's poverty-line use but wrong for a 20-year compounding loop, and which produced an ~11× wealth explosion and broke BU-monotonicity when tested naively. SIM_COST_SCALE no longer drives this formula at all as of v4.3 — it survives in the codebase only as a historical constant. This is not a "clean win": it redistributes as well as it grows — see Performance Comparison and CHANGELOG.md's v4.3 Release Notes for the full finding, including the Traditional Welfare Baseline's collapse to the wealth floor as a direct consequence of this same fix.
3. Heterogeneous AI Automation Displacement
4. FBS-Gated Advancement Probability
v4.0 note: this formula is now implemented in the simulation engine exactly as specified above (see runYear()). Prior versions (through v3.9) computed advancement from a fixed-probability heuristic gated on BLEI > 30 days instead of FBS — a gap identified in external review (Refine.ink, Aug 2026) between this specification and the shipped code. Y is WAGE_TO_USD-converted as noted under §1 above (was SIU_TO_USD through v4.3 — retired v4.4, see §1); EDC_residual uses the worked values from Table 1b (0.12 baseline, 0.025 for CCO+PTH participants) as a fixed proxy, not a separately-modeled consumer-debt submodel.
5. EDC-Adjusted Gini & BLEI Poverty
v4.14 note, prompted by an external audit's runYear()-level review: W_net below is a wealth STOCK minus EDC_full×Y×12, a one-year FLOW. That's a genuine conceptual distinction worth stating plainly rather than leaving implicit: this is not "wealth after all extractive costs" in a conventional net-worth-accounting sense — it is a wealth stock adjusted by one year's estimated extractive burden, a deliberately constructed inequality index in this framework's own convention, matching BLEI paper Table 6's specification exactly. The formula itself is unchanged; only the interpretation needed making explicit. See the interactive tool's own Gini KPI tooltip for the same clarification in-app.
v4.0 note: EDC-adjusted Gini is now actually computed by the simulation (previously the engine only ever computed nominal-wealth Gini; this formula existed here as a specification, not shipped code). v4.4 update: measured output from the current 5,000-run large-N study at the Full Integration reference configuration (500 agents, 20 years, seeds 1–5,000; see Performance Comparison below for full methodology and how these figures moved from the original v4.0 measurement through v4.3/v4.4) is Gini = 0.858 [95% CI 0.8577–0.8584] at the Traditional Welfare Baseline and Gini = 0.518 [95% CI 0.5179–0.5187] under full deployment — both meaningfully higher than the aspirational 0.44–0.46 / 0.22 range above, though both moved toward that range compared to the original v4.0 measurement (Baseline 0.800→0.858 is a move away, driven by v4.3's cost-anchor change pinning Baseline's wealth at the floor for effectively the whole population — see Performance Comparison; Full Integration 0.484→0.518 also moved away slightly, then improved again from a v4.3 peak of 0.536 once v4.4's wage-scale unification corrected the EDC-extraction calculation — see Version History for the full sequence). The direction (full deployment well below baseline) remains confirmed; the absolute levels remain off-target either way. The most likely source of the wealth-initialization contribution to this gap: lognormal(10.5, 1.2) has an inherent Gini coefficient of ≈0.60 before any simulation dynamics or EDC adjustment are applied (2Φ(σ/√2)−1 for σ=1.2) — already above the old baseline target, which suggests that target was a theoretical projection never checked against the actual coded initialization rather than a value the shipped model was failing to hit. Reconciling this — either by revisiting the wealth-initialization spread or by revisiting the target — is flagged as an open calibration item in CONTRIBUTING.md, not resolved here. v4.7 note: this Gini gap is about the wealth-init distribution's spread (σ) specifically — a separate, independently-found v4.7 finding concerns its level (μ) instead: the same lognormal(10.5, 1.2) draw's median (≈$36K) diverges roughly 5.3× from actual 2022 SCF median net worth (≈$193K). The two are mathematically independent (Gini of a lognormal has no μ term), so fixing one wouldn't touch the other — see Known Limitations, below.
6. EPPM and IPBI
7. Wage Growth Diminishing Returns & Recession Distribution (v3.3)
Internal Calibration Benchmarks#
These benchmarks function as internal calibration checks — they confirm the simulation reaches its design targets under optimal parameters. They are not external empirical validation criteria. All benchmarks must be satisfied for a parameter set to be considered fully calibrated for v3.3. The four v3.3 validation suite checks (BU monotonicity, participation threshold, CCO benefit direction, PTF inflation dampening) must all return PASS.
The Gini benchmark predates v4.0's actual implementation of EDC-adjusted Gini (see Mathematical Framework §5 above) — measured full-deployment output from the current v4.4 large-N study (N=5,000, see Performance Comparison) is 0.5183 [95% CI 0.5179–0.5187], not ≤0.22 (was 0.4843 under v4.0-v4.2 mechanics; moved through 0.5359 under v4.3 before settling here under v4.4's wage-scale unification — see Version History). Marked under revision pending the wealth-initialization/target reconciliation discussed there, rather than silently left as a passing claim.
An outside check, separate from these. The model has also been run through a scripted 2008–2012 recession and through 2020–2021 and compared with the changes in poverty that followed, using seven comparisons fixed before the run. It gets the direction right, the size too big and the timing too late, and its safety net absorbs almost none of a recession’s rise where the real one absorbed most of it. The comparisons, their verdicts and every figure are in Findings page.
Known Limitations#
The following limitations are documented as part of the ODD protocol (Grimm et al. 2010) and in the interests of transparent calibration. They identify directions for future model development and should be considered when interpreting simulation outputs.
- Limits of the v5.4 release engine, in short. (1) Households are fixed in the main reading: children stay children and couples neither form nor part, and partners are paired at random rather than by similar pay; the aging reading lets children grow up, be born and leave home, but with couples fixed for life the population drifts from the US mix over the years. (2) The comparison with current US policy and with other designs on the same simulated people is the compare page, which follows this release. (3) In the Adverse and Stress environments real wages fall in both runs (automation and recessions); the BU keeps its value because the Hub’s rule indexes it in years of high inflation. This is how those environments are built, not a forecast. (4) The no-program run starts from the survey’s wealth for each kind of household and lets people spend less when income is short, which brings debt by Year 7 much closer to the survey for couples; single parents and single adults are still far more often in debt than in the survey, and poverty spells are too short, because every adult starts with a wage and nobody is out of work for long, disabled or in a changing household (the US-data table above). (5) How much new output backs the Source’s payout is still the decisive unknown; with the Hub’s full list of what BU buy, few BU expire, so little project work backs the payout and prices rise faster than with the narrower list (a reading). The middle reading carries one study’s size (rural Kenya, a one-time transfer) to a far larger, yearly payout. (6) Aging changes the 40-year wealth result a lot (the aging reading), and results by age group exist only with it. (7) Readings whose size has no source are labeled where they appear: audits catching half of unearned rates (not specified by the Hub), the land tax falling on savings (the model has no land), and who pays the interest on savings that keep up with prices (the model has no bank). (8) Work (v5.4): people lose jobs and find new ones at the Current Population Survey’s rates, but how much lower pay is after a permanent job loss is a design parameter (a tenth on average, swept from none to a fifth), and it drives v5.4’s largest new result: the program’s wealth result in Reference. No one enters the labor force from outside, so unemployment sits below the official rate. Community businesses cut prices in food and utilities at the BLEI paper’s figures and nowhere else, because the Hub gives no figure for transport, health care or childcare; the sectors share one capacity rule. Insurance for community homes and taxes on unemployment benefits are not modeled. The Hub’s participation floor never comes into play in the released runs (Claude’s reading of the design: it is read on community-business membership, which stays below it).
- No demographic structure. The model does not represent age, retirement, disability, household composition, or life-cycle income profiles. All agents are treated as working-age adults. Age-stratified welfare analysis and retirement dynamics require an extension to the agent class. (v5.2: the release engine has aging, retirement at 67 with Social Security, and replacement of those who die, as a reading beside the main row; households, children and disability are still missing. v5.3: the adults live in households with their children in the main result; children’s lives (births, growing up, leaving home) and estates come with the aging reading; disability is still missing.)
- No direct agent-to-agent interaction. All social and economic effects are mediated through zone-level SZH parameters. Peer effects, social learning, and network contagion operate only via PTF adoption diffusion and recession propagation — not via bilateral agent interactions.
- "Recession" and "AI automation" are narrower than their names suggest (named explicitly, v4.14). Both mechanisms are real and calibrated, but each implements a reduced-form slice of the phenomenon its name evokes. Recession only ever multiplies earned wage income (
incomeShock); it does not touch asset prices, employment status, housing costs, business activity, government transfers, or interest rates — "exogenous labor-income shock" is a more precise description than "recession model." AI automation subtracts a displacement term directly from wage growth; there is no explicit job-loss event, job search, unemployment spell, or re-employment transition — an agent's income slows, it never actually disappears and returns. Both are common, legitimate first-pass ABM simplifications, and each is individually already flagged elsewhere in this list — this entry exists so the specific claim "this is a recession/automation model" isn't over-read as more general than either mechanism actually is. A fuller macro-shock model and an occupation-based re-employment model for AI are both real future-work candidates; see CONTRIBUTING.md's Model Architecture Feedback. - No global sensitivity analysis of the release engine yet. The earlier engine (v4.22) has a one-at-a-time sensitivity panel (v3.3: 11 mini-simulations, first-order only) and, since v4.6, a Latin-hypercube design export that a reader can run. The release engine is tested by its labeled readings (each one changes one choice, over 500 paired runs) and by the backing-share sweep, which do not capture how parameters interact. Morris screening and Sobol indices on the release engine are planned with the labor-and-markets round (v5.4). (Corrected in v5.2.2: this item still described the model before the change.)
- No external deployment validation. All calibration is against real-world analogues (Alaska PFD, CLT networks, Mondragon). No post-deployment data from a Compassionism implementation exists. Simulation outcomes should be treated as design-target projections, not empirical forecasts.
- 20% annual wage floor is a policy assumption. The minimum wage update of
max(income × 0.80, income × (1 + wageGrowth))prevents wage collapse below 80% of prior year. This is a deliberate policy boundary, not an emergent market outcome, and should be re-examined for scenarios modeling institutional failure or extreme automation. - Linear automation displacement model. Automation impact is modeled as a linear wage drag (population_rate × automationRisk) with no task-complexity structure, no new-job creation dynamics, and no occupational transition pathways. The model captures displacement magnitude only, not the labor market reallocation that historically accompanies technology transitions.
- Automation risk is drawn per adult, not from an occupation. Each adult’s
automationRiskis drawn from a 63%/37% mixture of Beta(6,1) and Beta(1,6) (since v4.20; 47%/53% from v4.3 to v4.19; a uniform draw on [0.2, 1.0] before v4.3), fitted to Frey and Osborne’s (2013) 702 occupations weighted by employment (node harness.js automationreproduces the fit). What remains: the risk is not tied to an occupation, so in the main row it is independent of the adult’s wage (across occupations the plain correlation with the log wage is −0.65; the v5.2 reading “closer to US data” links them with a copula, normal-score correlation −0.53), and hours and re-employment do not depend on it. Occupation-based risk comes with the labor-and-markets round (v5.4) and is a good first issue in CONTRIBUTING.md. (Corrected in v5.2.2: this item still described the model before the change.) - System Stability was a trend heuristic, not a structural metric (flagged v3.6, resolved v4.2). Through v4.1 the displayed value was
stabBase = improving ? 0.88 : 0.60(based on whether poverty fell over the run's final quarter), plus a small CIP bonus, plus bounded random noise — not derived from wealth variance, BLEI volatility, or agent-state transitions. As of v4.2 the metric is the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter — no RNG call, no manual per-mechanism bonus. See the v4.2 note in Methodology Overview. - Within-run random-number coupling across scenarios (found v4.2, resolved v4.3). Through v4.2,
runYear()drew a different number of random numbers for an agent depending on its branch that year, so runs with and without the program drifted apart agent by agent after year 0. Since v4.3 every random quantity in the yearly loop is drawn for every adult every year in a fixed order (eight draws per adult-year) and only its use depends on the adult’s state, so the paired runs stay paired adult by adult for the whole run; the unit suite counts the draws. Processes added since (aging, review errors) draw from their own random streams. One variable-length draw when the adults are first built is outside this guarantee and stays open in CONTRIBUTING.md. (Corrected in v5.2.2: this item still described the model before the change.) - The earlier engine’s large-N figures are history, not the release results. Since v5.0 the results on this page and the simulation page come from the release engine: 500 adults, 500 paired runs, three environments, 20 and 40 years (the findings explorer has every table). The earlier engine’s v4.4 study (5,000 adults per run; rechecked in v4.20 and v4.21) is kept in the version history under its own label, and its v4.1 40-year cohort extension was never reconfirmed under later mechanics. (Corrected in v5.2.2: this item still described the model before the change.)
- System Stability can score a stagnant floor as "more stable" than a thriving population. The structural metric (v4.2) is the inverse coefficient of variation of median wealth and BLEI over a run's final quarter — a genuinely flat trajectory scores as maximally stable whether that flatness reflects a thriving equilibrium or a population pinned at a hard floor with nowhere left to fall. In the v4.4 study, the Traditional Welfare Baseline — median wealth pinned exactly at the wealth floor in 100% of runs by year 20 — scores 99.0% stability, higher than Full Integration's 88.6%, precisely because a hard floor has near-zero variance left to measure. Read this metric as trajectory steadiness, not as an endorsement of the outcome it describes, and always alongside the poverty and BLEI figures next to it. Not previously disclosed on this page; added as part of the v4.4 sync (the interactive tool itself carries the same disclosure as of v4.4 — see CONTRIBUTING.md).
- Initial PTF share is not a hard cap (relabeled v3.6; settled by the release engine). In the earlier engine the PTF slider sets the year-0 adoption probability only, so the realized share can exceed it; since v4.6 an opt-in cap (off by default) makes it a ceiling. In the release engine PTF membership is limited by the capacity the PTFs have built (
dev/reports/02-production-side.md). What remains: once an adult joins a PTF they do not leave; PTF churn and capacity by sector come with the labor-and-markets round (v5.4). (Corrected in v5.2.2: this item still described the model before the change.) - The wealth update’s wage units (v4.0 simplification, resolved v4.3). v4.0 fixed the wage-to-dollar units in the BLEI, FBS and EDC-adjusted Gini but left the yearly wealth update on its old units. Since v4.3 the wealth update converts wages with the sourced constant
WAGE_TO_USD(Census CPS ASEC 2023 median personal income), used by every formula since v4.4, against the MIT living-wage basket (LIVING_WAGE_ANNUAL). What remains open is the wage distribution against the basket: the model’s median wage is below both the basket and the comparable US group’s median, so too many adults run into debt with no program (the US-data table; CONTRIBUTING.md). (Corrected in v5.2.2: this item still described the model before the change.) - Conversion and the money behind it (v4.0 gap; settled by the release engine). The earlier engine credits converted BU to the agent without debiting a treasury or production account. In the release engine the Source pays each conversion and keeps the conversion tax (its own ledger), the part of the payout backed by new output (capacity built and project work) is counted, and the rest is new money that the price module turns into higher prices; the cost ledger and the Source’s payout are checked by the unit suite. How much new output backs the payout is still the decisive unknown (the backing-share chart and the middle readings). (Corrected in v5.2.2: this item still described the model before the change.)
- System Stability needs at least ~8 simulated years to mean anything (found and partially fixed, v4.13). The metric is the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter. Before v4.13 that window was floored at a single data point, so every run of 5–7 years — the slider's own minimum is 5 — returned exactly 0.99, the metric's clamp ceiling, regardless of the trajectory: a variance of one observation is undefined, so the helper correctly returned zero volatility and the metric read it as perfect stability. The floor is now 2, which computes a real variance but is still a very small sample. Treat this metric as indicative at best on any run shorter than roughly 8–10 years, and prefer the 20-year reference horizon when the number matters. This sits on top of the separate, longer-standing limitation below — that the coefficient of variation cannot tell a thriving equilibrium from a population pinned at a floor.
- Poverty over time: who is poor, and for how long (v4.13 gap; settled by the release engine in v5.2). The earlier engine’s Cumulative Poverty Exposure sums each year’s poverty share, which gives the mean years poor per adult but not how those years are spread across adults. Since v5.2 the release engine records each adult’s poverty status every year and measures poverty spells (how many leave poverty in a spell’s first year, how many return), compared with the PSID on the US-data table; v5.2.1 shows each adult’s life path for one run on the simulation page. The wealth-poverty figure still rests on
POVERTY_LINE, whose provenance is open (below). (Corrected in v5.2.2: this item still described the model before the change.) POVERTY_LINE($25,000) has no citation anywhere in the codebase (logged v4.10, surfaced at the constant itself v4.13). Unlike almost every other calibration constant in this framework, the wealth-poverty threshold behind the headline "Wealth Poverty Rate" — and behind the wealth-exposure figure added in v4.13 — carries no source, no "framework spec" label, and until v4.13 no code comment at all. It is a pure post-hoc reporting threshold with a single read site, so this is a reporting-clarity gap rather than a mechanics risk (and the v4.10 Threshold Sensitivity charts exist precisely so a conclusion need not rest on it), but it is worth knowing when quoting the number: for reference the 2026 HHS one-person federal guideline is $15,960, noticeably lower. Tracked as an opencalibration:item.- Wealth-initialization median cites Fed SCF 2022 but diverges from it by ~5.3× (new finding, v4.7). Agent wealth is drawn lognormal(
WEALTH_INIT_MU,WEALTH_INIT_SIGMA) = lognormal(10.5, 1.2), whose median is exactly e^10.5 ≈ $36K by construction, matching this framework's own long-standing "Fed SCF 2022" citation (see Agent Architecture, above, and the ODD panel in the interactive tool). The actual 2022 Survey of Consumer Finances reports median household net worth at ≈$193K (Kuhn & Ríos-Rull, NBER working paper, 2025, using 2022 SCF microdata). This is independent of the wealth-init distribution's already-documented inherent Gini ≈0.60, discussed under Mathematical Framework §5 — Gini of a lognormal is a function of σ alone (2Φ(σ/√2)−1, no μ term), so correcting the median would move absolute wealth-poverty rates (especially in early simulated years, against the fixed-dollarPOVERTY_LINE) without moving Gini at all. Not corrected in v4.7 — logged as an opencalibration:item in CONTRIBUTING.md's Model Architecture Feedback, given how foundational this single draw is to every downstream wealth, Gini, BLEI, and poverty figure the model produces.
Implementation Roadmap#
Implementation roadmap by phase
Phase 1 — Foundation (Years 0–2):
├── Pilot Programs: 10 communities, ~50,000 participants
├── Infrastructure: CIP digital platform, PTH legal structures, PTF cooperative frameworks
├── Investment: $200B initial capital allocation
├── Target: 60%+ BLEI Threshold achievement in pilot areas
│ (ε_food ≈ 1.50–2.00 before Mondragon-scale efficiency; Comfortable tier expected)
├── Metrics: actual PTF pricing data for ε validation; CLT-style AE tracking;
│ APF-analogue labor supply monitoring; BLEI poverty rate tracking
└── Failure mode monitoring: participation rate, network density, governance integrity
Phase 2 — Scaling (Years 3–5):
├── Expansion: 100 communities, ~5M participants
├── Integration: full CCO-PTF-PTH deployment, SZH zone formation
├── Investment: $500B total (additional $300B)
├── Target: 85%+ BLEI Threshold; BLEI poverty <10%; network density above θ-activation
│ (ε_food expected to reach 2.20–2.64 as cooperative efficiency scales)
│ (PTF market share target ~18% — under active calibration revision for v3.x)
└── Simulation update: recalibrate SIM_COST_SCALE and PTF_SHARE from pilot data
Phase 3 — National Deployment (Years 6–10):
├── Scale: 50M+ participants nationwide
├── Investment: self-sustaining through conversion tax revenue
│ (~60–65% participation at 12% conversion tax ≈ self-funding threshold)
├── Target: 97%+ BLEI Threshold, 84%+ Secure, 0.22 EDC-adjusted Gini, <5% BLEI poverty
└── Flourishing: near-poverty entry to Tier 5 within 28–47 months (sensitivity range)Assumptions, ODD protocol, calibration notes and version history (moved from the simulation page)
Until the release these notes sat on the simulation page, in its "Assumptions, ODD Protocol & Known Limitations" panel. They moved here unchanged so the page could keep a short list of limits. They describe the engine through v4.22; the release's mechanisms (the ESP split, the production side, the Source financing, joining and leaving, community business and housing costs, the spending rule and avoided public costs) are described in the repository's dev/reports/.
Purpose: Explore Compassionism parameter space; identify conditions under which poverty targets are met; provide comparable scenario analyses for policy discussion. Not a forecasting model. Entities: Households (agents) characterized by economic state. Scales: 100–2000 agents; 5–30 annual time steps. Each agent represents one adult household unit.
Per agent: wealth (USD), wage (SIU), octave (skill tier 0–maxOct), quality (1–9×), automationRisk — bimodal mixture, 63% Beta(6,1) / 37% Beta(1,6) (v4.20; 47%/53% through v4.19) (v4.12 fix: this line said "∈ [0.2,1.0]" — the retired pre-v4.3 uniform draw's exact bounds — while the Initialization card two rows down already correctly described the current bimodal mixture; an external audit caught the two cards contradicting each other within this same panel), inCCO/inPTF/inPTH (participation flags), buBalance, acreEquity. Aggregate observables: poverty rate, Gini, BLEI tier distribution, EDC, stability index.
Time step: one year. simulate() runs the Traditional Welfare Baseline's full trajectory, then CCO Only's, then Your Settings year by year — each on its own copy of the same latent population and its own copy of the same seeded stream (common random numbers). Within one runYear() call: A. Once per year, before any agent moves — (1) PTH appreciation bonus from SZH coherence; (2) the SZH synergy coefficient θ and the two terms it gates (PTF conversion bonus, SZH→PTF induction probability); (3) a snapshot of realized PTF membership; (4) realized PTH membership; (5) population-wide inflation damping, PTF then PTH, each scaled by its realized share; (6) dollarCost, the BASE_DAILY_COST-based figure FBS and PTH sizing use; (7) the recession income multiplier; (8) the live PTF count, only if the adoption cap is on; (9) the AI displacement rate; (10) the automatic-stabilizer terms (v4.19, inert unless switched on): the recession trigger, the effective BU allocation, and the emergency-enrollment line. B. Then each agent in turn, in array order — (1) NaN rescue, then a record of start-of-year wealth for the extreme-poverty overlay (v4.18; reporting only, read by nothing in the loop); (2) exactly eight RNG draws in a fixed order (wage variance, BU spend fraction, CIP quality bump, octave advancement, SZH→PTF induction, SZH→PTF share, PTH appreciation noise, PTF adoption), whatever the agent's status — the v4.3 common-random-numbers guarantee, machine-checked by the validation suite's invariants check since v4.16; (3) BLEI on the agent's start-of-year state; (4) wage growth, floored at 80% of last year's wage; (5) cost-reduction factor from start-of-year PTF/PTH/CCO membership; (6) wealth += wage income × incomeShock − inflation-adjusted LIVING_WAGE_ANNUAL × cost factor; (7) CCO participants only: BU balance decay plus the new allocation (capped at 3× monthly BU), spend, CIP quality bump, conversion at the octave/quality/Phi/PTF-capped rate less progressive tax (× incomeShock), then FBS-gated octave advancement using the new wage, then SZH→PTF induction; (8) PTH members: tenure +1, payment→equity contribution, appreciation; (9) agents not yet in PTF, from the second year on: innovation + distress + Bass adoption check; (10) wealth floor clamp. Timing consequences: an agent who joins PTF in a given year gets its cost relief from the next year; adoptions within a year do not change that year's Bass imitation term (a start-of-year snapshot) but do count against the optional cap immediately; FBS compares this year's grown wage with a cost factor set by start-of-year membership; and agents never read one another's state except through the two start-of-year membership counts and the live cap counter — so array order matters only when the PTF cap is on. (New v4.16 — the ODD standard's third element, previously missing from this panel. Enumerated line by line against runYear() as CONTRIBUTING.md Good First Issues item (e) asked, rather than transcribed from an audit's attempt.)
Emergence: Poverty, inequality, and BLEI tier distributions emerge from agent-level rules — not directly imposed. Adaptation: Octave advancement gated by FBS (Mullainathan & Shafir bandwidth constraint). PTF adoption via economic distress and social diffusion (Bass 1969 model). No explicit utility maximization — behavioral rules only. Feedback structure (v4.14, named explicitly per an external audit's runYear()-level review): runYear() is not a set of independent annual adjustments — several mechanisms couple into genuine reinforcing loops. Welfare (BLEI, computed from the PRIOR year's state) drives this year's wage-growth bonus; the resulting wage feeds FBS; FBS gates octave advancement; and octave itself then feeds BACK into wage growth (WAGE_OCTAVE_BONUS) and raises the CCO conversion-rate ceiling for the following year — so two otherwise-identical agents who diverge in octave once can diverge further in both wage and conversion capacity thereafter. A parallel loop runs through housing: PTH lowers annual cost, which raises FBS, which raises advancement probability, which raises wages — making PTH a liquidity-and-mobility mechanism as well as a housing subsidy. Neither loop is a bug; both are the intended behavioral design. The practical implication is that a reported "CCO effect" or "PTH effect" is a compound effect (currency/cost relief + financial bandwidth + skill advancement + wage growth + conversion compounding), not a single isolated channel — worth keeping in mind when reading any headline delta, and a candidate for formal pathway decomposition in a future release (see CONTRIBUTING.md's Model Architecture Feedback).
Interaction: Indirect market-mediated only — no direct agent-to-agent interaction. Cooperative effects via PTF/SZH zone parameters. Stochasticity: Seeded PRNG (mulberry32); wage variance ±10%; recession events 10% annual probability (beta(5,2) severity); BU spend rates 60–90%; octave advancement (FBS-gated probability). Same seed + params → identical run.
Agent wealth: lognormal(CFG.WEALTH_INIT_MU,CFG.WEALTH_INIT_SIGMA) → median ~$36K ⓘ. Wage: lognormal(3.5,0.5) → median ~33 SIU. Octave: beta(2,5)×maxOct. Quality: lognormal(log(maxMult×0.5),0.4). automationRisk: 63%/37% bimodal mixture, Beta(6,1) high-risk / Beta(1,6) low-risk, drawn by inverse CDF with exactly two random numbers, independently of wage (v4.20: weight recalibrated from 47% to match the employment-weighted mean of Frey & Osborne's 702 occupations; bimodal since v4.3, uniform[0.2,1.0] through v4.2 — see Cumulative Bug Fixes). λ (FBS capability coefficient, v4.0): uniform[0.0001654,0.0013233] per agent (v4.4: rescaled ÷6.0456 from the pre-v4.4 [0.001,0.008] — see Cumulative Bug Fixes). Participation flags assigned at initialization probabilities. Baseline, CCO-only, and Main are instantiated from one shared canonical latent population (v4.2) — same n individuals, three policy configs — with no initial-wealth adjustment applied to any of them as of v4.4 (previously baseline agents received an undocumented-provenance ×0.85 wealth haircut; removed — see Cumulative Bug Fixes).
Annual update (runYear): the full verified order — the once-per-year population terms, the fixed eight-draw RNG schedule, and the SZH→PTF induction step this summary used to omit — is now in "Overview: Process Overview & Scheduling," above (v4.16). In brief: wage growth with diminishing returns → living cost (LIVING_WAGE_ANNUAL, inflation-adjusted, × the agent's cost-reduction factor) → CCO conversion at an octave-capped rate → FBS-gated octave advancement → SZH→PTF induction → PTH equity contribution and appreciation → PTF adoption (innovation + distress + Bass imitation) → wealth floor clamp. BLEI = (liquid + γ·wage·WAGE_TO_USD + buFood) / dailyCost (v4.0: wage converted to USD before summing; v4.4: onto the same real-dollar anchor the main loop uses — see Empirical Calibration below). Gini (v4.0): computed on EDC-adjusted net wealth, W_nominal − EDC×Y×12. Recession: beta(5,2) incomeMultiplier ∈ [0.70,0.95], NBER-calibrated (nber.org) — mode 0.90 (10% loss), mean ≈0.88 (~12% loss). Population-wide inflation is damped by PTF and PTH presence, each scaled by that system's REALISED population share (PTF since v4.14, PTH since v4.15 — previously the initial PTF slider and a flat PTH toggle factor respectively; see Cumulative Bug Fixes). (v4.12 fix: this card described the main wealth loop as SIM_COST_SCALE-driven and BLEI's wage term as SIU_TO_USD-converted — both accurate for v4.0-v4.2 but superseded by v4.3's wealth-loop unit fix and v4.4's SIU_TO_USD retirement respectively; an external audit caught this ODD panel had never been updated for either change, even though the "Cumulative Bug Fixes" entries for both releases, further down this same panel, were.)
The front door now opens with a statement of what Compassionism aims to do, and the comparison has a second view: besides equal cost, each comparator at the size its proponents propose ($12,000 a year of basic income; a negative income tax guaranteeing the 2026 poverty line; the $80,000 stakeholder grant of 1999). Because larger programs usually cut more poverty, every costed row now shows its change in poverty severity per $1,000. Compassionism gains a row with a needs-based top-up at the same cost, a tenth of the allowance moved to extra BU for low earners. The public grocery network appears as New York City plans it, five stores serving about 0.5% of shoppers, and at full scale. X-Cents adds its Power of 1, one day a week on which coins buy food at $1 each. The design panel describes BU as a restricted currency the design both supplies and creates demand for, the octave as conversion capacity, and the octave wage raise and inflation damping as the model's stand-ins, theoretical and yet to be empirically tested.
The page now opens with a question and a table that runs Compassionism beside a basic income, a negative income tax, an asset endowment, a public grocery network and X-Cents, on the same simulated adults under the same rules: every program dollar counted at face value, every design paid for by the same flat contribution on wages, and the same work response to every dollar that does not depend on work. Wherever Compassionism appears, the row beneath it switches off its two theoretical mechanisms, yet to be empirically tested (the octave wage raise and the PTF/PTH inflation damping, both the model's stand-ins rather than the design). The table is pre-computed by the open testbed in harness.js and does not run live, and it uses the testbed's profile, which differs from the live scenarios in ways its caption lists, so its figures differ from theirs. A panel under it describes each design as proposed, how the model simplifies it, and its source. domtest.js Phase 11 checks the question, the data, the row order and the links.
Every tooltip was a CSS ::after box drawn inside the element it explains. Inside the comparison table, which scrolls sideways on narrow screens, the scroll box clipped anything above its top edge, so the first lines of each column tooltip were hidden behind the column labels. The same boxes, though invisible, still took up space: at a 390 px phone width the page measured 421 px and scrolled sideways. One floating tooltip, placed by script in window coordinates, now serves every ⓘ, preset and link tip: it cannot be clipped, it opens below its host when there is no room above, it stays inside the window, and it closes on Escape. At 390 px the page is now 390 px wide (checked in a headless Chromium; domtest.js has no layout engine, so it checks the behavior, not the pixels). The CSS boxes remain only as a fallback when scripts do not run.
v4.19 made CCO's cost relief scale with BU: 20% of the basket at $1,200. The $1,200 is a year-0 amount, but the rule read the nominal BU against it and applied the share to the nominal basket, which already rises with prices. So under inflation an unindexed BU kept its full real relief, although the COLA note said it erodes; and with a cost-of-living adjustment the share itself grew with prices, to 26.3% of the basket by year 20 in Adverse Environment. The share now reads BU in year-0 dollars, divided by the same price index the basket uses: without COLA the relief falls to 15.2% of the basket by year 20 there, and with COLA it holds at 20%. Nothing changes at 0% inflation, so Full Integration, CCO Only, High Automation and the seed-42 regression are bit-identical. Over 500 seeds Adverse Environment's wealth poverty rises from 38.4% to 40.3% and its median wealth falls from $169,911 to $146,364; Stress Test's wealth poverty rises from 61.2% to 62.0%. COLA's benefit was overstated, most at high inflation: at 5.5%, Full Integration's gain from COLA falls from 11.8 to 7.0 points of wealth poverty. harness.js keeps the old rule behind RELIEF_PRICE_LEGACY for comparison.
An external audit pulled Frey & Osborne's 702 occupation probabilities with 2016 employment counts and found the high-risk mixture weight low. v4.20 checked all 702 rows against the paper's own appendix (every SOC code, rank and probability matches) and fitted the weight: 47% had been F&O's headline share of employment above 0.7, reused as a mixture weight, which gives a mean risk of 0.479 against the data's 0.592. The weight is now 63%, which matches that mean. Separately, drawAutomationRisk() drew its components through beta(), whose rejection sampler uses a variable number of random numbers, so the weight shifted every later draw of agent construction: changing it moved the seed-42 regression even with automation off. Both components are now drawn by their exact inverse CDF, two random numbers per agent, so the weight affects only runs with AI automation on. The switch itself moved the seed-42 regression once, to 15.8% wealth poverty, $570,661 median wealth, 1,975d median BLEI and Gini 0.518 (v4.19: 16.6%, $559,223, 1,965d, 0.534): the same population statistics, a different random realization. harness.js validate checks both the new figures and that a legacy switch reproduces v4.19 exactly.
No slider, toggle pair or seed field had an accessible name: the label sat in an adjacent <span> that nothing referenced. Every slider now points at its label with aria-labelledby and announces its formatted value (e.g. "$1,200") through aria-valuetext; each On/Off pair is a labeled group. The ⓘ caveats and preset descriptions were CSS hover-only, unreachable by keyboard: each ⓘ is now focusable with its text as its accessible name, the preset and view buttons carry theirs as a description, and every tooltip opens on keyboard focus. Sliders had no visible focus indicator (outline:none with no replacement); they now show one. Separately, a shared link with a malformed seed showed "NaN" (?seed=abc), or "12" while running unseeded (?seed=12abc); the readout now reflects the value the run actually uses. The Automatic Stabilizers panel's labels and hints were also rewritten in plain language, with COLA spelled out as cost-of-living adjustment. None of this touches the engine.
Implements the Research Hub's crisis protocols (Integrated Implementation Roadmap, Appendix G) as opt-in levers in a new Automatic Stabilizers panel: a BU increase in recession years (fixed multiplier, or scaled to the recession's income loss) above a minimum-loss trigger; suspended BU expiry and emergency enrollment of non-participants while triggered; and BU indexed to inflation above a trigger. Each works through a per-year effective BU amount inside runYear() and draws no random number, so runs with and without them stay common-random-number paired. All are off by default, so the seed-42 regression is unchanged. A new Shock Response card reports this run's tally and, on demand, a 30-seed paired study that measures what a rule offsets and finds the shock-neutral multiplier for your settings. CCO's cost relief now scales with the BU issued (Duke's decision; see Known Limitations), which leaves every $1,200 preset unchanged; the calibrated defaults are ×1.35, or +2.8% BU per 1% income loss. The Poverty by Five Measures card was also reworded for clarity. A reader note on place-based BU premiums, which the engine cannot simulate, sits in the Shock Response card.
A fifth measure counts people who are homeless, with necessities provided by charity if at all, including people unhoused because of serious mental illness and people who live this way by choice. The engine has no housing, mental-health or vocation state, so it is an expected share layered on engine state from three pathways: economic (scales with housing distress), serious mental illness (reduced only by PTH + SZH wellness zones) and voluntary (policy-invariant). Reporting only: no RNG, nothing read back, the seed-42 regression unchanged. The card, now Poverty by Five Measures, takes the page's scenario colors, groups its rows, and adds row-scaled bars and colored change cells.
domtest.js dispatched DOMContentLoaded on document; the page listens on window, and a non-bubbling event never reaches it. The check therefore only compared static markup with META. It now stales every label first and fires the event where the page listens. The tab title is now filled from META too, and the structured-data description, still describing v4.16, is current.
v4.16 updated META.VERSION but not the header or footer, which were hardcoded — the single-source-of-truth rule META was created for in v3.5 had never been applied to the page's own markup. Every visible version label now reads META.VERSION at load, and domtest.js checks it.
The run CSV listed every run parameter except BU Expiry, now added. The LHS Sensitivity export sampled ptfShare in every row but inherited PTF's on/off toggle from the page, so with PTF off the whole column was inert; PTF is now on whenever the sampled share is above 0 (the same convention CCO already used), and the CSV records every fixed setting the rows inherit. Neither change moves any documented figure.
Every headcount through v4.16 was a stock measure (wealth, or BLEI's wealth-based runway). A new card adds relative income poverty (cash income below 60% of the median, with and without in-kind cost relief) and basket poverty (income below the living-wage basket, net and gross of cost reductions), each with its year-0 starting point and change against both year 0 and the Baseline. Reporting only: the income flows are recorded as the engine computes them, with no RNG and nothing read back.
A sixth preset applies the Stress Test's environment to the unchanged reference settings, separating environmental stress from weaker settings. The 55% CCO participation level and the θ density threshold were described as dynamics; neither is one (see Known Limitations), and the run warning, sensitivity table and comments now say so.
The Contributing guide's reproducibility promise — same seed and parameters, same output — held only for a run started from a quiet page. simulate() yields to the browser between simulated years and assumed nothing else touched the shared RNG global in the meantime. Two things did: the attribution ablation left its own stream installed between chunks and, when finished, restored a stream saved before the new run began; and every validation check called initRNG() without restoring. Reproduced in a headless DOM against the unmodified v4.15 page: a seed-42 Full Integration run started while the previous run's attribution chart was still computing gave median wealth $545,506, and one started during the validation suite $555,354 — not the documented $559,223. Both windows were reachable in normal use (the Run button stays enabled during each), including the second or so after the page's own reference run. Fix: each simulated year re-asserts its own stream; runSim() hands its stream to simulate() explicitly; the ablation restores the global after every chunk and abandons itself if a newer run has started (a run-generation counter, which also stops a stale attribution chart overwriting a newer run's); each validation check restores the global it found. In an uninterrupted run every change is a no-op, so the seed-42 regression is unchanged; domtest.js now reproduces both failure modes and fails against v4.15.
Since v4.0 the Baseline has used a fixed 3% CPI while the tested scenario uses the inflation slider (0% in Full Integration). Matched at 0%, Baseline wealth poverty is 50.5% rather than 71.8%, and its median is pinned at the wealth floor in 0.6% of runs rather than 100% (seeds 1–500). About 24–38% of the headline poverty gap is this inflation difference. A new opt-in toggle, off by default, runs the Baseline at the slider's rate; a run warning discloses the mismatch whenever it exists; CSV/JSON exports and the run configuration line record the Baseline rate used. Full account in Known Limitations, below; the choice of default is logged for Duke.
Written line by line from the code and cross-checked by the validation suite's new eight-draws-per-agent-year assertion. The Submodels card's one-line summary had omitted the SZH→PTF induction step and every once-per-year population term; it now points to the full schedule.
The "Full integration outperforms baseline" check compared the Reference preset (shocks off, 0% inflation) with the Baseline preset as-is (shocks on, 3% inflation), so three things differed at once, and the shock-on arm's recession draws broke common-random-numbers pairing; it now matches p1's shock and inflation settings and still passes 5/5. The invariants check adds: BU balance in [0, 3×BU], Acre Equity non-negative, every λ in range (which keeps P(advance) in [0,1)), same seed reproduces identical agent wealth, a different seed changes it, exactly eight RNG draws per agent-year across four presets with the PTF cap off and on, and the PTF cap's documented ceiling. The suite still reports six checks.
(1) A comment inside runYear() still said BLEI/FBS/Gini used the retired SIU_TO_USD — a fifth stale claim in that function the v4.13 sweep missed. (2) Known Limitations said recession "only ever multiplies earned wage income"; it also scales CCO conversion proceeds. (3) The Empirical Calibration PTH card, rewritten in v4.15, said the non-liquid remainder of appreciation goes to acreEquity; the code adds all of it there and credits the liquid share to wealth. The documentation now describes the code; whether the code should change is measured and logged as a decision in CONTRIBUTING.md.
An external audit (MuseAI) found that the line directly beneath the v4.14 PTF fix has the same defect class, in a coarser form: if(p.pth&&inflRate>0)inflRate*=0.90 applies a flat 10% reduction to the ENTIRE population's cost inflation whenever PTH is toggled on and inflation is nonzero — regardless of whether 5% or 50% of the population is actually in PTH. Unlike the PTF line beside it (which at least read a slider, if the wrong one — see the v4.14 entry below), this line referenced no adoption-scale quantity at all. Confirmed directly, isolating the computation from every downstream mechanism: at inflRate=2%, the damping factor was identically 0.90× whether 5%, 20%, or 50% of a 500-agent population was in PTH. Unlike PTF, PTH membership doesn't grow organically during a run — it's a single Bernoulli draw at construction (see instantiateAgent()), never revisited — so there's no "actual current adoption" to track across years the way ptfAdoptFrac tracks PTF's growth; the fix computes pthMemberFrac the same way, once per year (no RNG, a count of already-set a.inPTH flags), and scales the damping by it: inflRate*=(1-pthMemberFrac*0.10) replaces the flat *=0.90. The 0.10 coefficient (i.e. 1−0.90) is UNCHANGED from the historical value, preserved as the ceiling at 100% PTH membership — exactly as the v4.14 PTF fix preserved its own 0.5 coefficient and only swapped the input variable. Verified to have zero effect on every documented figure: Full Integration and hiAI have inflRate=0; Baseline and CCO-Only have pth=false — the guard never fires for any of them, confirmed via the unchanged seed-42/Full Integration/20yr regression, below. The only shipped preset this touches is Stress Test (pth:true, 10% uptake, 2% inflation); the only VAL_TEST this line reaches is ptf_infl — both its scenarios share the same reference pthUptake=20%, so the fix scales them identically and the test's own pass/fail is unaffected, re-verified 5/5 after the fix. Effect size where it does apply, seed 42/20yr, harness-computed with recession shocks and AI automation held off (this harness's simplified recession handling doesn't reproduce the in-app preset's stochastic-shock path — a documented scope limit of harness.js — so these are effect-size illustrations of the fix, not the in-app Stress Test run's own output): Stress Test preset moves poverty 41.8%→43.2%, Gini 0.696→0.702, median wealth $158,155→$143,494, median BLEI 482d→436d — worse, not better, since realistically-scaled damping is WEAKER than the flat toggle overstated it to be at a 10% uptake rate (the opposite direction from the v4.14 PTF fix, which strengthened damping because real adoption ran above its slider). A synthetic isolate (PTH + 4% inflation only, no PTF/SZH/CIP) shows the same direction more cleanly: poverty 43.4%→45.2%, Gini 0.688→0.701, median wealth $157,016→$118,888, median BLEI 568d→456d. A PTH-uptake sweep (5%→100%, seed 42) confirms the scope: identical at every uptake when inflation is 0; identical at 100% uptake for any inflation (the new formula equals the old flat one at full membership); diverging most at low uptake. Separately, PTH switched on with ZERO members no longer damps inflation at all (previously the flat 0.90 fired anyway). Ported into harness.js for parity.
Found while the same audit was reviewing the v4.14 expiry fix: Math.max(1,p.expiry) returns NaN, not 1, if p.expiry is ever undefined — Math.max propagates NaN from any argument rather than ignoring it. Every current caller always supplies expiry explicitly (the slider, pBase/pCCO in simulate(), mkMiniP()'s hardcoded default), so this was never a LIVE defect on any documented or shipped path — not a bug that moved a number anywhere — but it sat one un-set object key away from a quiet failure for anyone building a new test or mini-scenario object by hand and forgetting the key: decay would be NaN, totalBU/totalConversion would become NaN, and — because the pre-existing isNaN rescue on a.wealth resets it to 0 — every CCO participant's wealth would be silently zeroed each year (median wealth $0 in a 200-agent check), with no error thrown. p.expiry||1 is a no-op for every value 1 through 6 already in use — confirmed directly: decay at expiry=1 and expiry=6 is identical before and after — and expiry=0 (never reachable from the UI) already resolved to decay=0 via the pre-existing Math.max(1,...) floor and still does. A missing value now behaves as expiry=1. Ported into harness.js for parity.
The Run 10×/50× Stochastic CI display computed ci=1.96*s/Math.sqrt(n) for every n — correct only in the large-sample limit. At n=50 (the 50× button) this is a minor understatement (z=1.96 vs. the correct Student's t(49)≈2.01, about 2.5% narrower); at n=10 (the 10× button, the more commonly used of the two) it's a real one — the correct multiplier, Student's t(9)=2.262, is about 15% larger than 1.96 (equivalently, the displayed interval was about 13% too narrow), so a nominal 95% interval actually covered only about 91.8% for a normally-distributed statistic (about 94.4% at n=50). Display-only — reads already-computed MR.results, draws no RNG, touches no agent state, cannot affect any simulation output or the documented regression table. Fixed with tCritical95(df): a standard, uncontroversial two-tailed-95% Student's t table (df 1–30, plus common larger anchors through 120), linearly interpolated between listed df and converging to the z=1.96 asymptote for df>120 (within ~1% of the exact value beyond df=120; all 36 table entries were checked against an independent implementation of the t quantile function, and linear interpolation between them errs by at most 0.0014). The header/subtitle copy now states the actual t-value and degrees of freedom used for the run shown, rather than a fixed constant that didn't match what was computed for every n.
v4.12 corrected three places where the ODD Protocol and Empirical Calibration panels described pre-v4.3/pre-v4.4 mechanics as current (see those entries, above and below). An external audit found a fourth instance the same sweep missed: the Empirical Calibration panel's "PTH Acre Equity — payments now build equity" card (below) still described housing cost reduction as "35% of SIM_COST_SCALE" (SIM_COST_SCALE is read by no function in this file — see the "historical constant" note on that card, elsewhere in this panel — the actual mechanic is a dimensionless cf*=0.65 factor applied to the main loop's LIVING_WAGE_ANNUAL-based cost) and a flat "wealth += appreciation × 0.5" 50% haircut — superseded by v4.5's tenure-cohort ramp (pthLiquidShare(), 15% at tenure=1 rising to 85% at tenure≥5; see that entry, above). The card's remaining sub-claim — a 25% equity-contribution share — was and remains accurate. Corrected in place below, following the same practice as the three v4.12 fixes: documenting what was wrong rather than silently swapping the text.
A two-pass external ChatGPT audit (general review, then a focused runYear() mechanics review) found that the "BU expiry (months)" slider — presented as 1-6 — collapsed to a step function in the code: var decay=p.expiry<2?0:0.7 means expiry=1 gives decay=0 and EVERY OTHER value (2, 3, 4, 5, 6) gives the identical decay=0.7. Confirmed directly: expiry=3 and expiry=6 produced bit-identical simulation output before this fix — a six-position slider implementing only two distinct behaviors. Replaced with decay=1-1/expiry, a continuous annual-approximation interpretation (the same documented-interpolation pattern pthLiquidShare() already uses for a comparably under-specified continuous quantity), rather than rebuilding monthly BU tranches — the audit's own suggested "proper" fix, but a materially larger change to a loop that runs on an annual cadence; logged as a future refinement in CONTRIBUTING.md, not attempted here. Verified to have zero effect on anything documented: at expiry=1 — the value every one of the five shipped presets, every VAL_TEST/OAT/LHS mini-simulation, and every documented regression/large-N figure uses, since none of them ever varies this slider — the new formula gives decay=1-1/1=0, identical to the old behavior. Isolated before/after check at seed 42 (CCO-only, no PTF/PTH, so the effect is attributable to this one mechanism): expiry=3 and expiry=6 now diverge as they should (wealth $402,103 → $401,139/$406,164 respectively; poverty 25.0% → 25.0%/24.6%), while expiry=1 is bit-identical to before. Ported into harness.js for parity.
The same audit's runYear() review caught a genuine state-variable/effect mismatch: if(p.ptf&&inflRate>0)inflRate*=(1-p.ptfShare*0.5) damps inflation using p.ptfShare — the STATIC initial-adoption-probability slider — while the per-agent cost factor a few lines later correctly gates on each agent's REAL, current a.inPTF flag. This codebase already computes the actual population-wide adoption fraction, ptfAdoptFrac, a few lines further down — for the Bass-diffusion adoption term — but never reused it for the inflation effect, even though the UI's own PTF-share tooltip already states organic adoption "can exceed this value" (v3.6+). Fixed by moving ptfAdoptFrac's computation earlier in the function and reusing it for both purposes — it draws no RNG and reads only already-set a.inPTF flags, so this reordering shifts no draw, anywhere, for anyone. Verified to have zero effect on every documented figure: Full Integration, CCO-Only, Baseline, and hiAI each have inflRate=0 or ptf=false at this exact line, so the guard never fires for any of them — confirmed via the unchanged seed-42/Full Integration/20yr regression, below. The only shipped preset this touches is Stress Test (ptf:true, 2% inflation); the only VAL_TEST is ptf_infl, re-run and confirmed still passing 5/5 after the fix. Effect size where it does apply, seed 42/20yr: Stress Test moves poverty 43.0%→41.8%, Gini 0.703→0.696, median wealth $141,814→$158,155 — actual adoption running above the 8% slider (via Bass diffusion and economic-distress adoption over 20 years) means stronger damping with the fix than without, in the economically expected direction. Ported into harness.js for parity.
The same audit asked for an in-app diagnostic showing how much of the Baseline-vs-Your-Settings gap reflects WEALTH_FLOOR's exact value rather than a genuine policy effect — a fair question this project had previously answered only offline, via CONTRIBUTING.md's four-floor-value sensitivity sweep (v4.8). Two new KPIs show the share of the final-year population pinned exactly at the floor, for Your Settings and for Baseline, as a same-run companion to that offline sweep — not a replacement for it, since seeing today's pinning doesn't show how SENSITIVE that pinning is to the floor's own value, which the sweep alone answers. Pure snapshot of already-final agent state — no new simulation, no RNG. Exported to CSV and JSON.
No code or mechanics changes in any of these — see the in-app locations named for each. EDC-adjusted Gini is now explicitly labeled, in its KPI tooltip and in the code's own comment, as a constructed inequality index (wealth — a stock — minus one year of EDC-implied extraction — a flow), not conventional net-worth accounting; the formula is unchanged and still matches BLEI paper Table 6 exactly. A dual-cost-anchor table (LIVING_WAGE_ANNUAL vs BASE_DAILY_COST — what each drives and why they're deliberately different concepts) was added to the Empirical Calibration card. The ODD panel's Design Concepts card, and an inline comment at the octave-advancement site inside runYear() itself, now name the CCO→FBS→octave→{wage growth, conversion ceiling} and PTH→FBS→octave→wage feedback loops explicitly — the audit's own runYear()-level review correctly identified that these aren't independent annual adjustments but genuine reinforcing loops, meaning a headline CCO or PTH effect is a compound of several coupled mechanisms, not one isolated channel. A new Known Limitations entry states that "recession" only ever shocks earned wage income and "AI automation" is a reduced-form wage-growth penalty — both real and calibrated, both narrower than their names might suggest. The standing "not a forecast" banner gained one sentence making explicit that results demonstrate the implications of the implemented assumptions, not the empirical validity of those assumptions.
(1) A shared "Copy parameters as URL" link silently disabled every preset tooltip. applyParamsFromURL() reset each preset button's class to a bare pbtn to clear the "active" highlight — which also stripped .tip-host, the class the v4.5 preset-description tooltips are attached to. Any visitor arriving through a shared link therefore lost all five preset hover descriptions for the entire session, with no error and nothing visibly wrong. This is the same bug v4.5's own entry below records catching in applyPreset(); the identical pattern was introduced in applyParamsFromURL() in that same release and survived seven subsequent audits, because the shared-link entry point is the one path none of them exercised. (2) The parameter-sensitivity panel duplicated its tip box on every run. renderSens() wrote the "💡 One-at-a-Time (OAT) Sensitivity" note with insertAdjacentHTML('afterend', …) — which inserts a sibling of #sens-inner, so the function's own innerHTML reset never cleared it, and renderSens() runs once per completed simulation. Three runs in one session left three stacked copies. Now a single stable-id node, created once and updated in place. Neither defect is visible from reading the source — both were found by loading the actual page into a headless DOM and exercising it, then kept fixed by automated checks in that same harness (before: 3 tip boxes after 3 renders, pbtn tip-host → pbtn; after: 1 and 1, class preserved). That harness is new in v4.13 and is the first time this project has been able to verify DOM behavior at all, rather than carrying the standing "not verified in an actual browser" caveat every UI change since v4.8 has had to. It does not verify CSS layout or Chart.js pixel output, which still need a human look.
structuralStability() measures the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter, with the window floored at one data point. The Simulation-years slider's own minimum is 5 — and for any run of 5, 6 or 7 years, Math.floor(years/4) is 1, so the window held a single value. coeffVar() correctly returns 0 for a single sample (the variance of one observation is undefined), giving 1/(1+0)=1 for both series and a final score of exactly 0.99 — the metric's own clamp ceiling — for every 5–7 year run ever executed by this tool, whatever the underlying trajectory. Verified directly rather than inferred: a strictly-increasing five-point series scored 0.990000, as did every other short series tried. This is the same failure the Known Limitations panel already describes for a floor-pinned population ("flat outcomes score as maximally stable"), but arising from having no data in the window rather than from the data being flat — and it was reporting a maximum where the honest answer is "not enough of the run to measure." The window floor is now 2. Scope, checked rather than assumed: a sweep across every supported run length confirms lengths 8–30 are bit-identical to v4.12 (at 8 years floor(8/4) is already 2), so every preset, the seed-42/20yr regression baseline, and every OAT/LHS/ablation/validation mini-run at 10 or 20 years is unaffected; only 5–7 year runs change, and only in this one displayed number.
Every poverty figure this tool reported was a final-year snapshot. Two runs can end with the same headline poverty rate while having put wildly different numbers of agents through wildly different numbers of poor years to get there, and nothing on the page showed that — a gap the v4.12 external audit raised directly (its "person-years in poverty / time-to-tier-attainment" suggestion). Two new KPIs report the mean number of years an agent spends below the wealth-poverty line, and below the BLEI poverty line, across the whole run, for Your Settings against the Traditional Welfare Baseline. The derivation is exact, not an approximation: each year's figure is a share of the same population, so Σt sharet = (1/N)·ΣtΣi 1[poori,t] — the average years-poor per agent. At the seed-42 reference configuration the final-year wealth-poverty rate is 16.6%, but the average agent spends 4.8 of 20 years below that line (Baseline: 11.8); BLEI-poverty exposure is 3.7 years against the Baseline's 11.1. Purely a reporting transform of arrays the engine already computes — the only new computation anywhere is one extra bleiMetrics() call per baseline year to get the baseline's annual poverty share (its annual median was already tracked, and a median cannot give a headcount); bleiMetrics() is a pure function that draws no RNG and mutates nothing, confirmed by the unchanged regression. One honest limit, also stated in Known Limitations: this is aggregate exposure, not a spell-length distribution — it cannot distinguish "everyone poor for 3 years" from "30% of agents poor for 10 years," which would need per-agent longitudinal tracking this engine does not keep.
An external audit of v4.11 suggested the Threshold Sensitivity charts "test for first-order stochastic dominance over the displayed range... a clear statement when curves cross... do not imply a universal welfare ordering when curves cross." Both callouts underneath the charts now end with a "Dominance check" line, computed by checkDominance(), stating plainly whether Your Settings sits at-or-below Baseline (and separately, CCO Only) at every threshold shown, or whether the curves cross — and if they cross, roughly where. This makes exact and explicit a comparison the charts already implied but left to the reader to eyeball; it reports honestly in both directions, not only when the expected scenario wins. At the seed-42 Full Integration reference configuration, Your Settings dominates both comparisons on both charts in both views (headcount and gap) — a genuine, checked finding, not an assumption baked into the feature, confirmed this session via a Node.js harness reproducing the documented regression figures exactly before the dominance check was run against them. A crossing case was also confirmed reachable by the same code path against a hand-built adversarial curve pair (not real simulation output) during unit testing, and a Stress Test preset re-run (seed 42, 40% participation, lower BU, higher tax, automation and shocks conceptually enabled — though this specific harness re-run held shocks off, since the harness's simplified recession handling is a documented limitation, see harness.js's own scope note) still showed dominance rather than a crossing, meaning readers should not expect this feature to manufacture drama that isn't in the model — it reports what the curves actually do, which for this model's mechanics is usually (not always, by construction) a clean win for whichever scenario has more active systems. Purely a reporting-transform addition — computes no new agent state, draws no RNG, and reads only the same sorted/prefix-summed arrays every other part of this feature already built; cannot affect the Main run, Baseline, or CCO-Only trajectories. Validated against 9 constructed test cases (both dominance directions, identical curves, a single crossing with its location correctly identified, weak dominance with tied values, and floating-point-noise edge cases correctly NOT flagged as spurious crossings) before being run against real seed-42 curve data. See CHANGELOG.md's v4.12 Release Notes for the full validation trail.
Duke relayed the v4.10 feature to an external AI system for a methodology critique (see CHANGELOG.md's v4.11 Release Notes for the full exchange and this project's now-standard verify-before-acting treatment of it). The review reconfirmed the Atkinson (1987) citation as correctly used, confirmed the World Bank multi-line comparison as apt, and recommended one concrete addition: "two distributions can have identical headcounts at every line yet one can have systematically larger gaps" — the existing % curves (the headcount ratio, FGT-0 in the poverty-measurement literature) can't distinguish a scenario where the poor are barely below the line from one where they're far below it. A new "Avg. shortfall" toggle (setThreshView()) switches both existing charts to a poverty-GAP view (FGT-1: Foster, Greer & Thorbecke, 1984, "A Class of Decomposable Poverty Measures," Econometrica 52(3), 761-766) — the mean per-capita shortfall below each threshold, in the same dollars/days as the axis itself (agents at or above the threshold contribute zero). Deliberately reports the absolute gap, not the textbook-standard threshold-normalized ratio: normalizing by the threshold breaks down at threshold≤0, which the wealth chart's own range deliberately includes (WMIN=-$20,000, so Baseline's WEALTH_FLOOR-pinned distribution renders as a real step rather than an off-chart flat line — an existing v4.10 design choice, unaffected here) — the absolute-dollar/day framing sidesteps that division problem entirely while staying at least as interpretable ("the average dollar short, per person, if the line were drawn here" reads as a direct "cost to close the gap" figure). buildPrefixSum()/povertyGapAvg() (defined alongside povertyCDF(), above) keep the same O(log n)-per-threshold-query complexity budget via a one-time O(n) prefix-sum pass per distribution — not a linear scan per point on the curve. Validation: the core function was checked against a naive brute-force reference (literal sum(max(0,threshold-x_i))/N, no indexing tricks) across 22,210 random trials — random continuous distributions, duplicate-heavy arrays specifically mimicking WEALTH_FLOOR pinning (Baseline's actual shape), BLEI-shaped right-skewed arrays, and degenerate edge cases — zero mismatches beyond ordinary floating-point rounding, before this ever touched the shipped file. After integration, a full end-to-end Node.js test evaluated the actual edited script against the documented seed-42/Full Integration/20yr configuration and independently cross-checked povertyGapAvg()'s output against a discretized numerical integral of the already-validated povertyCDF() curve (mean absolute gap at z equals the integral of the CDF from -∞ to z — a genuine mathematical identity, not an approximation of the production code itself); the one point where this cross-check initially disagreed (immediately adjacent to WEALTH_FLOOR, where 68.6% of a Baseline population sample sits at one exact value) was tracked to a discretization artifact in the CROSS-CHECK's own coarse numerical integration near a sharp jump discontinuity, confirmed by refining that check's resolution (error shrank 0.171→0.017→0.0016 as the check's own step count increased 20,000→200,000→2,000,000) and by an independent direct per-agent sum matching povertyGapAvg()'s output exactly — the production function was correct throughout; only the auxiliary cross-check's resolution needed refining to see it agree. Purely a reporting-transform addition, same guarantee as v4.10: computes no new agent state, draws no RNG, cannot affect the Main run, Baseline, or CCO-Only trajectories. Chart.js rendering/DOM behavior itself is not verified in an actual browser — same sandbox limitation as every CSS/UI change since v4.8.
Rather than reporting poverty at one committed dollar figure (CFG.POVERTY_LINE) or BLEI-day cutoff, two new charts (renderThresholdSensitivity()) show the population's full cumulative distribution across a RANGE of thresholds at once, for Baseline/CCO-Only/Your Settings simultaneously — the "poverty dominance" approach from the poverty-measurement literature (Atkinson, A.B. (1987). On the measurement of poverty. Econometrica, 55(4), 749-764; also the World Bank's own practice of reporting global poverty at several lines at once, e.g. $2.15/$3.65/$6.85/day). If one scenario's curve sits below another's across the whole range shown, it reduces poverty regardless of exactly where a reader believes the "true" line sits — directly responding to two calibration disputes this project has already had over a single committed dollar figure (see LIVING_WAGE_ANNUAL's history, v4.3 and v4.9) without resolving either one. A new CFG.FED_POVERTY_LINE_1P constant ($15,960/yr, the 2026 HHS ASPE guideline for a one-person household) is shown as labeled callout text for external comparison — same "reference only, not a simulation parameter" category as CFG.NORDIC_GINI elsewhere in this file; verified this session directly against a primary source after a first fetch attempt returned a stale cached 2017 snapshot of the ASPE page (caught and cross-checked against an independently-dated PDF rather than used — see CHANGELOG.md's v4.10 Release Notes for the full trail). Purely a reporting transform of already-simulated final-year wealth/BLEI values — computes no new agent state and draws no RNG, so it cannot affect the Main run, Baseline, or CCO-Only trajectories; confirmed via a full end-to-end Node.js integration test (not just a syntax check) that drives the actual makeLatentPopulation()/instantiateAgent()/runYear() functions against the documented seed-42/Full Integration/20yr configuration and cross-checks the new povertyCDF() function's output against calcMetrics()/bleiMetrics()'s own existing figures at the model's default thresholds — exact match (16.6% wealth poverty, 13.6% BLEI poverty). Deliberately NOT in scope: CFG.LIVING_WAGE_ANNUAL — unlike POVERTY_LINE (a pure post-hoc reporting threshold, exactly one call site, inside calcMetrics()), LIVING_WAGE_ANNUAL is genuinely baked into runYear()'s wealth-accumulation trajectory; exploring it this way would mean re-running the simulation per value, not re-reading an already-computed array, and is scoped as separate, larger, mechanics-adjacent follow-up work — see CONTRIBUTING.md's Model Architecture Feedback. Chart.js rendering/DOM behavior itself is not verified in an actual browser — same sandbox limitation as every CSS/UI change since v4.8.
CONTRIBUTING.md's "λ Calibration Status" note recommended reporting the FBS gate's λ coefficient via its half-saturation bandwidth, FBS₅₀=ln(2)/λ — "an explicit reference bandwidth at which a median-capability agent has even odds of advancing," easier for a reviewer to interrogate than a raw 1/USD coefficient — and had already hand-computed the resulting figure in prose (≈$524–$4,191/month). CFG.FBS_HALF_SAT_LO/HI now compute that range directly from FBS_LAMBDA_HI/LO (current values: $524–$4,191/month), so it can't silently drift out of sync if the λ range is ever revised again. This is a reporting change only — makeLatentAgent() still draws λ directly and uniformly from FBS_LAMBDA_LO/HI exactly as in v4.5; no per-agent λ value, RNG draw, or simulation output changes. A genuine reparameterization — sampling FBS₅₀ as uniform and deriving λ=ln(2)/FBS₅₀ from it, rather than the reverse — would change the shape of the λ distribution (a uniform-in-1/x transform isn't uniform in x) and is a distinct, larger design decision needing its own validation pass; tracked as a separate open item, not attempted here.
Model Architecture Feedback flagged that "the PTF slider sets the t=0 adoption probability only... a quota-based or feedback-clamped adoption model remains open," since organic per-agent adoption (distress + Bass diffusion, plus a separate SZH→PTF induction pathway) has no ceiling and realized share can run past the slider indefinitely — confirmed directly: an uncapped 20-year Full Integration run at seed 42 reaches 53.4% realized PTF share against an 18% slider. A new "Cap adoption at slider value" toggle in the PTF section, off by default, makes the slider a genuine ceiling when enabled: both adoption pathways check a shared, continuously-updated live membership count and stop firing once realized share reaches the configured Initial PTF share, agent-by-agent within a year rather than only once per year. Correction (v4.12): this entry previously stated that the seed-42 capped result was 23.8% — an external audit flagged this as inconsistent with CONTRIBUTING.md's own account of the same feature (19.0%), and a direct Node.js re-run of this exact configuration this session confirmed CONTRIBUTING.md was right and this panel was wrong: 23.8% was the output of an earlier, discarded once-per-year-checked prototype, not the shipped agent-granularity implementation described in this same paragraph. Re-run correctly: seed 42's year-0 Bernoulli draw already lands at 19.0% (95/500 agents) — above the 18% slider — so ptfCapAllows() blocks every subsequent admission for the entire 20-year run and the final share holds at 19.0%, not 53.4%, but also not exactly 18.0%. This is the "it doesn't retroactively correct sampling variance already present in that year-0 draw itself" caveat the in-app toggle tooltip already states, playing out at this specific seed. A 10-seed sweep (seeds 1–10) run this session confirms the general mechanism works as designed: seeds whose year-0 draw starts below 18% (6 of 10 tested) grow and stop within one admission of the 18.0% line itself — in every one of those cases landing at exactly 18.0%, since 500×0.18=90 is a whole number here — while seeds whose year-0 draw already starts at or above 18% (the other 4) hold at that starting level for the whole run, exactly as seed 42 does. See CHANGELOG.md's v4.12 Release Notes for the full verification trail. Implemented as a use-side gate on RNG draws (uPtfAdopt, uSzhInduce, uSzhPtfShare) runYear() already draws unconditionally every year regardless of branch (the v4.3 RNG-coupling discipline) — enabling the cap adds no new RNG call and does not shift stream position, so every existing preset, the seed-42 regression baseline, and every OAT/validation/ablation mini-simulation (none of which set this flag) are byte-identical to v4.5. Only runs that explicitly opt in are affected. Surfaced in run-config, CSV export, and the run-warnings banner when active, so a capped run is never silently indistinguishable from an uncapped one in exported data.
Implements the "LHS Sensitivity Export" spec from Good First Issues: a stratified Latin Hypercube design over the same five parameters and ranges OAT already perturbs (BU, participation, Initial PTF share, inflation, conversion tax — LHS_RANGE, shared in spirit with OAT's own RANGE so the two can't quietly diverge), reusing mulberry32 for the design draw itself (seeded at a fixed value so the design's stratification is reproducible run-to-run, independent of the page's own seed field). Each of the LHS_N design points (100 by default) is run as its own 200-agent/10-year mini-simulation — all under the same fixed seed (7777, matching OAT's own convention of holding the stochastic realization fixed so cross-row variation is attributable to parameters, not RNG noise) — and the design matrix plus outcome metrics (poverty, BLEI, participant/non-participant poverty) are exported as CSV. This is deliberately not a Sobol sampler: genuine Sobol indices need paired Saltelli-structured samples beyond a single LHS design; LHS gives well-stratified parameter-space coverage suited to the external Python/R analysis this button hands off to, which is exactly the gap CONTRIBUTING.md's spec described. A new "🧮 Export LHS Sensitivity Design" button appears in the sidebar alongside OAT after a run completes.
v4.3 added per-agent pthTenure tracking as a first step toward this (see below); v4.5 implements the haircut itself via pthLiquidShare(tenure) — a linear ramp from 15% at tenure=1 (the closest available point to the BLEI paper Table 1a's "6 months" anchor, given tenure is tracked in whole years) to 85% at tenure≥5 (the paper's "5+ years" anchor), replacing the flat 50% share of annual Acre Equity appreciation counted as liquid wealth used through v4.4. Both anchors are the midpoint of the paper's cited range (10–20% / 80–90%); the paper gives two qualitative points, not a closed-form curve, so the ramp in between is a documented interpretation choice — see CFG.PTH_LIQUID_SHARE_*. Since PTH membership doesn't change once assigned, most PTH agents spend the majority of a 20-year run at the mature 85% rate (16 of 20 years) — higher than the old flat 50% for most of the run, lower only in years 1–4. Isolated effect at N=300 seed-paired runs (same population/RNG, only this mechanism toggled): median wealth $523,958→$526,941 (+0.6%), median BLEI 1,820.6d→1,826.0d (+0.3%); Gini, wealth poverty, BLEI poverty, and flourishing rate unchanged to the precision reported. Seed-42 Full Integration reference: median BLEI 1,951d→1,965d, median wealth $558,001→$559,223. All six VAL_TESTS pass 5/5 post-change. Also fixed in passing: instantiateAgent()'s pthTenure:inPTH?0:0 ternary (identical branches, a cosmetic no-op noted but left alone at v4.4) simplified to pthTenure:0 — no output effect.
A button next to the Run controls serializes the current sliders, toggles, and seed into a query string and copies the resulting URL via the Clipboard API (falls back to logging the URL to the console if the API is unavailable). Loading a shared URL applies its params on top of the Full Integration defaults — via applyParamsFromURL() — before the page's own auto-run, so a link only needs to specify what differs from the reference config, and unrecognized or malformed keys are silently ignored rather than erroring. The page-load "illustrative reference run" banner and forced seed-42 only fire when no recognized params were found in the URL, so a shared link runs with its own seed (or none) instead of being silently overridden by that default.
The N=5,000 harness-based cohort study CONTRIBUTING.md has carried since v4.3 (see below) now also appears as a final-year table in the results panel — SSI-level, SSDI-level, and Retirement-level rows (agents tagged by year-0 wage at or below each anchor's SIU-equivalent, per CFG.SS_ANCHOR_*, unchanged methodology), each with population share, wealth poverty, BLEI poverty, median BLEI/tier, and top-tier rate. Cross-sectional by design, matching CONTRIBUTING.md's own method — cohort membership is fixed at year 0 and not tracked as a time series — computed via ssAnchorCohorts() from the same calcMetrics()/bleiMetrics() calls the headline KPIs already use, without threading new arrays through step()/finish()'s existing positional-parameter chain. Single-run figures shown here will naturally vary seed-to-seed, especially for the small SSI-level cohort (≈0.8% of a 500-agent population, roughly 4 agents at default settings) — CONTRIBUTING.md's own N=5,000 table remains the citable figure; this view is for exploration, not a replacement for it.
An ⓘ next to the tier-distribution chart's title now shows each tier's day-range on hover (Crisis <7d, Precarious 7–29d, Threshold 30–119d, Stable 120–364d, Secure 365–729d, Flourishing 730d+). Each of the five preset buttons now shows a one-sentence description of what it actually configures and why, on hover, reusing the same tooltip technique as the existing KPI-badge ⓘ icons (generalized into a new .tip-host CSS class since preset buttons aren't inline text links). Caught in testing: applyPreset()'s active-button styling directly overwrote className on every preset click, which would have silently stripped the new tooltip class the first time any preset was selected — fixed as part of this change, not a separate regression.
v4.0 fixed wage/USD unit mixing inside BLEI, FBS, and Gini using SIU_TO_USD ≈16.63 — a cost-side-derived approximation invented because no real wage-side dollar anchor existed yet. v4.3 later added WAGE_TO_USD ≈100.52, a genuine Census/BLS-sourced anchor, but scoped it to the main wealth loop only. The result: the same agent's wage resolved to two different real-dollar figures depending which formula asked it — at the median wage, ~$3,518/mo for wealth accumulation versus ~$582/mo for welfare measurement, a ~6× divergence (external audit finding). CFG.SIU_TO_USD is retired; agentBLEI()'s income-buffer term, calcBLEIComponents(), the FBS gate's Yusd, and the EDC-adjusted-Gini calculation all now use WAGE_TO_USD. agentEDC() was and remains unaffected — it's a dimensionless SIU/SIU ratio that never touched either constant. This is not a compounding-loop change like v4.3's fix (these formulas are recomputed fresh each year, not accumulated), but it does feed back into wealth accumulation indirectly through the FBS gate's octave-advancement probability — see the paired fix below. At N=5,000: Full Integration median wealth rises modestly, $495,411→$526,120 (+6.2%, not a repeat of v4.3's much larger jump — this fix mostly corrects a measurement formula, not the wealth-accumulation mechanism itself); BLEI poverty falls 17.1%→12.5%; wealth poverty falls 19.5%→15.4%; Gini falls 0.536→0.518 (both Full Integration and CCO-only became more equal, not less). Effect is most visible on wage-poor cohorts specifically: the Social-Security-anchored cohorts (below) see their BLEI buffer term corrected from a ~6×-understated figure to their real income — SSDI-level median BLEI 16.4d→22.6d, retirement-level 16.6d→a median-of-medians 25.1d (mean 32.0d — see the cohort note below on why median, not mean, is the fairer summary for a right-skewed quantity across a small per-run cohort). None of these cohorts reliably cross the 30-day Threshold in the typical case; the qualitative v4.3 finding — Full Integration substantially helps this population without lifting it out of poverty by the simulation's own tier definitions — still holds, modestly narrowed, not reversed. Participant poverty (wealth) falls 15.4%→10.0%; non-participant poverty is exactly unchanged, 34.3%→34.3% — a clean confirmation the fix is behaving as intended, since non-participants never execute the FBS-gated code path this fix touches. Full N=5,000 methodology and tables in CONTRIBUTING.md.
The FBS gate's λ coefficient has units of 1/USD by its own original comment — it multiplies a dollar-denominated surplus (fbs) to produce an advancement probability. Once fbs's dominant input (Yusd, above) grew ~6× under the wage-scale fix, λ's effective scale was left uncorrected, pushing pAdvance toward saturation (≈1) for nearly all agents regardless of BU level — measurably weakening the BU-monotonicity regression check (one seed dropped from a clean win to an exact tie at n=200). FBS_LAMBDA_LO/HI are rescaled ÷6.0456 (the precise WAGE_TO_USD/legacy-SIU_TO_USD ratio) from 0.001/0.008 to 0.0001654/0.0013233, preserving the HI/LO=8× heterogeneity shape. This entry originally called that rescale "dimensionally-necessary" — that overstated it. fbs mixes the wage-derived term that changed scale with fixed-dollar terms (BU face value, basic cost) that didn't, so fbs itself doesn't scale by one clean factor; the rescale is a chosen behavioral recalibration to restore useful advancement-probability variation, kept because the un-rescaled range — matching the BLEI paper's own published λ — already saturates (89.8–100% advancement probability) at the paper's own worked example, leaving little of λ's intended role as an individual-capability differentiator. All six VAL_TESTS pass 5/5 post-change — internal consistency, not empirical validation of these specific values. Still spec-derived, not independently sourced; open calibration status documented in CONTRIBUTING.md and the BLEI paper's own Index IV note.
v4.2 gave the three comparison scenarios one shared latent population, but their year-by-year trajectories still ran on three independently-seeded RNG streams (seed+700001 baseline, seed+700002 CCO-only, unoffset for Main) — decorrelated from each other and from Main, not just from population construction (external audit finding). Since v4.3 made runYear() draw a fixed count and order of RNG calls per agent per year regardless of which policy toggles are active, three independent mulberry32(seed) closures — same seed, no offset — necessarily produce identical draw sequences at each (agent, year) position; baseline and CCO-only now use the same unoffset seed Main already resolves to, rather than their own dedicated streams. All three scenarios now experience identical per-agent "luck" each year (wage-variance draw, BU-spend fraction, adoption checks, appreciation noise), isolating the policy effect from Monte Carlo noise as CRN pairing is meant to. This has no effect on Main's own single-scenario trajectory — mulberry32() returns a fresh, independent closure on every call, verified directly, so creating one for baseline/CCO-only doesn't touch Main's stream position. The ablation engine already used this same same-seed-across-scenarios pattern successfully (seed0+500000 for every ablation arm); this brings the main 3-way comparison in line with it. Population construction (+700003) is unaffected.
Baseline agents shared the same latent draw as CCO-only/Main (v4.2's paired population) but received an extra ×0.85 wealth adjustment at construction, labeled only "lower savings absent CCO" with no external citation. Since CCO hasn't happened yet at year 0, applying this before any dynamics run embedded part of the treatment effect into the counterfactual's own starting point — breaking the "matched initial agents" premise the paired-population design exists for (external audit finding). Removed; baseline now starts from literally the same wealth as its matched counterparts. Effect on baseline's own year-20 outcome is minimal — median wealth remains pinned exactly at WEALTH_FLOOR, now confirmed in 100% of N=5,000 runs (v4.3 described "more than half" the population pinned; this is the same underlying finding, now precisely quantified) — because the binding constraint is the ongoing annual cost-vs-wage gap under 20 years of 3% CPI compounding, not the year-0 starting point. Reinforces, rather than resolves, the open WEALTH_FLOOR reconsideration flagged below.
Baseline hardcoded automation:false regardless of the user's setting, while CCO-only already mirrored it — a genuine confound whenever High Automation is enabled, since Baseline would then face a different exogenous world than the scenario it's being compared against (external audit finding: "exogenous conditions belong to the world, not the policy"). Now mirrors the user's setting like CCO-only does. No effect on the standard Full Integration reference configuration or on VAL_TESTS, both of which default automation off; only matters when a user explicitly enables it.
The general-purpose gamma(a) rejection sampler divides by (a−1) inside a log term; at a=1 exactly this is x/0, making the acceptance test 0×log(∞)=NaN and therefore always false. Every call exhausted all 5,000 rejection iterations and fell through to return a — gamma(1) returned the literal constant 1 on every single call (confirmed: 20/20 identical samples in testing), not a random Exponential(1) draw. This directly narrows beta(6,1)/beta(1,6) — drawAutomationRisk()'s high/low-risk components, used once per agent on every simulation run — and wasted the full 5,000 dead iterations per call in the process (≈2.7s of pure waste per 500-agent run, browser or harness). Fixed with the exact closed-form solution: Gamma(shape=1,scale=1) is exactly Exponential(1), so -ln(RNG()) replaces the rejection loop entirely for this case — standard, not a new formula. Population construction is now roughly 200× faster as a side effect, which is what made this release's N=5,000 study practical to run. Verified against all six VAL_TESTS post-fix.
The test's own code comment always correctly described its comparator as zeroing PTF/PTH/SZH/CIP alongside CCO, not holding them fixed — but the user-facing name ("...does not worsen when CCO is enabled") read as a clean, CCO-isolated claim, and the v4.2 changelog entry describing its introduction made the same overclaim (external audit finding; corrected above). Renamed to "Non-participants under Full Integration vs. all-mechanisms-off baseline" — string-only, the comparison logic, threshold, and pass/fail behavior are unchanged.
The wage(SIU)/wealth(USD) mismatch in the main annual wealth update — left unfixed at v4.0 because a full fix proved dangerously sensitive to the conversion constant — now has a validated fix. WAGE_TO_USD ≈ 100.52 converts wage to USD (Census/BLS CPS ASEC 2023 median personal income, $42,220 ÷ 35 SIU × 12); LIVING_WAGE_ANNUAL = $49,370 anchors the cost side (a national living-wage figure for a single adult, no children — World Population Review's MIT-sourced state table, unweighted 51-jurisdiction average; see CONTRIBUTING.md for the population-weighting caveat). This replaces netting against BASE_DAILY_COST, a bare-subsistence floor correct for BLEI's poverty line but wrong for a 20-year compounding loop — that anchor produced an ~11× wealth explosion and broke BU-monotonicity (2/5 seeds) when tested. All six VAL_TESTS pass under the shipped anchor (5/5 BU-monotonicity). This is not simply a bigger-numbers fix — it redistributes. At N=5,000: Full Integration median wealth rises from $80,008 to $495,411, but EDC-adjusted Gini rises from 0.479 to 0.536 and wealth poverty rises from 14.4% to 19.5% — low-wage and non-participating agents don't share proportionally in the correction the way median-and-above-wage agents do. The Traditional Welfare Baseline is affected even more severely: median wealth falls to exactly the wealth floor (−$10,000, i.e. more than half that scenario's population is now debt-ceiling-bound by year 20), since a median wage earner's income no longer covers the new cost anchor even before the baseline's pre-existing 3% CPI compounding widens the gap further. The near-poverty (low-wealth) cohort, by contrast, benefits enormously — its median member now reaches Flourishing within 20 years in every tested run, versus never within 40 years pre-v4.3 — because most of its members have ordinary wage draws and this fix rewards wage level specifically, not starting wealth. See the new Social Security-anchored cohort study, below, and CONTRIBUTING.md's Model Architecture Feedback for the full picture, including two new open questions this surfaces (whether WEALTH_FLOOR is still appropriately calibrated, and whether the framework needs a mechanism targeting low-wage populations specifically).
The v4.2 population-pairing fix gave scenarios an identical initial population but runYear() itself still consumed a variable number of RNG calls per agent depending on which conditional branches that agent took each year (participants triggered more draws than non-participants), so agent-level trajectories still decorrelated after year 0 whenever participation composition differed. Every conditional quantity inside the per-agent loop (BU spend fraction, CIP quality bump, octave advancement, SZH→PTF induction, PTH appreciation noise, PTF Bass adoption) is now drawn unconditionally, with only the use of the drawn value gated — mirroring the v4.2 fix to agent construction. The non-participant-poverty validation check, loosened to a mean-across-seeds criterion at n=500 when this coupling was first found, is tightened back to strict per-seed agreement at the suite's standard n=200 (5/5 in testing).
Replaces uniform[0.2,1.0] — flagged as a simplification since v3.4 — with a 47%/53% mixture: high-risk agents ~ Beta(6,1) (mean ≈0.857), low-risk agents ~ Beta(1,6) (mean ≈0.143), sourced to Frey & Osborne (2013) and Autor (2015) (~47% of US employment above a 70% computerization-probability threshold, 702 O*NET occupations). Verified to have no material effect on aggregate outcomes in isolation, before being combined with this release's other changes. (v4.20: that 47% is a share above 0.7, not a mixture weight; the weight is now 63% — see the v4.20 entry above.)
Agents now carry pthTenure (years held in PTH residency), incremented while in PTH and reset on exit. This is tracking data only — the tenure-cohort-varying liquidity haircut itself (BLEI paper Table 1a: ~10–20% at 6 months rising to ~80–90% at 5+ years) is not implemented this pass; the flat 50% haircut is unchanged. Tracked as a good-first-issue in CONTRIBUTING.md now that the data is flowing. v4.5 update: implemented — see the tenure-cohort liquidity haircut entry below.
Prompted by a review question: median-income comparisons don't show how the framework treats its actual target population for a poverty-elimination claim. SSA's 2026 COLA Fact Sheet gives three real income anchors — SSI ($994/mo), SSDI average ($1,630/mo), average retirement benefit ($2,071/mo) — now in CFG.SS_ANCHOR_* and used in a harness-based N=5,000 cohort study (agents tagged by initial wage at or below each anchor's SIU-equivalent, tracked with identical mechanics to the rest of the population — this is not a simulated Social Security mechanism, the model has no age/disability/retirement structure). Result: Full Integration dramatically improves this population's BLEI relative to baseline (20–30×) but does not lift it out of poverty by the simulation's own tier definitions — median BLEI in the teens of days for all three anchors, deep in the Precarious tier. Full table and discussion in CONTRIBUTING.md. v4.5 update: now also surfaced as an in-app final-year cross-section — see the Cumulative Bug Fixes entry below and the results panel's "Social Security-Anchored Cohorts" table. v4.4 update: these exact figures moved under the wage-scale unification (above) and should not be read as still-current — see that entry for the corrected N=5,000 numbers (median-of-medians BLEI now ~20-25d across the three anchors, no longer literally "teens of days" for all three; the relative-improvement multiplier over Baseline also fell from ~20-30× to ~7-10×, since Baseline's own BLEI is computed with the same corrected wage-buffer term and rose too, from near-zero to a few days. The qualitative conclusion — substantial relative improvement, no cohort reliably escapes poverty by the tier definitions — is unchanged; only the specific multiplier and day-counts are stale here.)
The project README.md had not been updated since v3.3 and stated several claims this pass corrected against the actual source: the CCO conversion formula as C = B₀ × 2^O (actual mechanism is linear in octave, see the note above), Gini as a closed-form decay expression (the actual metric is the standard Gini computed on EDC-adjusted net wealth in calcMetrics()), the recession severity mode as ≈0.86/14% loss (corrected below to 0.90/10%), and PTF's role as a "40-60% overhead reduction" plus a "2.64x BU food premium" (PTF is a 12-16% cost-reduction mechanism only; 2.64x is CCO's ε_food, an unrelated parameter). The README's Output Metrics table previously presented internal design-target constants as if they were achieved simulation output; it now cites the 5,000-seed large-N study (seeds 1-5000, matched Full Integration / CCO Only / Traditional Welfare runs) published in the Replication Framework's Performance Comparison section, with a note on where measured output still falls short of the original design targets (Gini and wealth-poverty rate) and the likely cause (the wealth-initialization distribution's own inherent Gini ≈0.60 pre-dynamics).
The main run, "CCO Only," and baseline comparisons now share one canonically-drawn latent population instead of three independently-sampled ones — closing an item raised in the Aug 2026 external review ("the baseline and CCO-only populations are not actually the same individuals") and in CONTRIBUTING.md's Model Architecture Feedback. The earlier objection that pairing "isn't well-defined once maxOct/maxMult differ in support between scenarios" is correct against the scaled OUTCOME (octave/quality) but not the underlying draw: the pre-policy shape variate is now shared and scaled independently per scenario, which is what a "same individuals, different policy" comparison needs. This also fixed a genuine, independently-found bug: makeAgent()'s three eligibility draws used short-circuit &&, so the number of RNG calls consumed during agent construction depended on which subsystems were toggled on — two scenarios built from the "same" seed but different toggle states (e.g. the validation suite's PTF-on vs PTF-off check) silently diverged in every draw after the first toggle difference, not just membership. Reproducibility note: this changes the RNG call sequence — a given seed no longer reproduces v4.1's numeric output. New seed-42 reference figures are in the version-history comment block, near the top of the source.
Replaces the trend-plus-noise heuristic flagged since v3.6 (see below) — a CONTRIBUTING.md good-first-issue. The displayed value is now the inverse coefficient of variation of median wealth and median BLEI over the final quarter of the run (no RNG call, no manual per-mechanism bonus): if a mechanism genuinely stabilizes outcomes it now shows up endogenously as lower volatility, since every mechanism already feeds wealth and BLEI within runYear(). The baseline and CCO-Only comparison rows, previously hardcoded at "~65%"/"~88%" regardless of actual configuration, now compute the same metric from their own trajectories. This formula has its own limitation, surfaced and disclosed in v4.4 — see Known Limitations.
The original four checks each ran once, on one hardcoded seed — unable to distinguish a genuine structural property from a lucky draw. All four now run across five seeds and require unanimous agreement (all four are 5/5 robust). Two checks were added: a CONTRIBUTING.md good-first-issue verifying non-participant poverty doesn't worsen when CCO is enabled (compares against an all-mechanisms-off baseline, not a true CCO-isolated counterfactual — PTF/PTH/SZH/CIP are zeroed in the comparator, not held at the tested scenario's rates; renamed for accuracy in v4.4, see Known Limitations — uses a larger population and a mean-across-seeds criterion rather than per-seed agreement — see the code comment above VAL_TESTS for why, a newly-documented RNG-coupling property of runYear() under Known Limitations), and a structural-invariants check (population conservation across BLEI tiers, octave/wealth-floor/participation bounds, Gini bounds) as a regression net for future changes. Renamed "Internal Validation Suite" → "Internal Consistency & Behavioral Test Suite" and labeled against the ODD Level 1–5 validation hierarchy (this suite covers Levels 1–3; Levels 4–5, external empirical validation, remain open — see Known Limitations) since "validation" on its own overstated what these checks establish.
Title, meta description, and social-share tags previously called this a "policy-grade" simulation while the CSV export and Known Limitations already called it an exploratory ABM not intended as a forecast — an internal inconsistency. Now consistently "research-oriented exploratory agent-based simulation... for comparative policy analysis" throughout, matching the more defensible framing that was already used elsewhere in this document.
The v4.0 paired-population ablation redesign (below) intentionally builds every removal scenario's agents under the FULL config so membership flags (inCCO/inPTF/inPTH) match across scenarios for RNG-pairing — correct for the simulation mechanics, which already re-check the relevant system toggle everywhere they consume those flags. But agentBLEI()'s SZH+PTF synergy term trusted a.inPTF directly with no ptfOn parameter to check against, unlike ccoOn/pthOn, which already gate a.inCCO/a.inPTH everywhere they're used (independently re-audited in this pass and confirmed unaffected). Result: the "Remove PTF" ablation result could retain a synergy bonus it shouldn't have whenever SZH was also active — worth roughly 12 BLEI days per affected agent in testing — understating PTF's true contribution on the Attribution chart. Fixed by threading ptfOn through agentBLEI()/bleiMetrics(), mirroring how pthOn already works; normal (non-ablation) runs are numerically unaffected. Found in a Claude (Anthropic) chat audit, Aug 2026.
IS_REF_RUN was set true by the page-load auto-run (v3.4) but nothing ever read it, so the "📊 Illustrative reference run — seed 42…" banner stayed display:none permanently since its introduction — general-purpose neighbors (the Exploratory-simulation notice above the KPIs) covered for it, which is likely why this went unnoticed across several audit passes. Wired up in finish(): the flag is read once, to show the banner only for the genuine auto-loaded run, and reset immediately after so it can't linger into a later manual or chained Monte Carlo run.
Several small strings had drifted from the mechanics they describe. The recession hint stated a 2–4 year / 65–85% income-multiplier range; the actual beta(5,2) formula (unchanged since v3.3, and correctly described elsewhere in this panel) produces 1–3 years / 70–95% — apparently a leftover from before that v3.3 change, missed by every subsequent audit including the one that corrected this same distribution's mode figure elsewhere on this page (below). The Max Octave hint described conversion capacity as "C = C₀ × 2ⁿ"; the actual v4.0 mechanism (above) is linear in octave/maxOct, reaching exactly the quality multiplier ceiling at max octave — an exponential form would hit 512× at octave 9, far past that ceiling — so the hint now describes the real shape. The Quality Multiplier Ceiling's static default read "14.58×" against the correct "14.56×" (9×1.618) shown one field above it; cosmetic, since updateEffMax() corrects it as soon as the reference preset auto-loads, but wrong in markup. The poverty-chart baseline caption asserted baseline recessions are unconditionally "enabled," no longer accurate once v4.0 paired baseline's shock exposure to the user's own toggle. OAT sensitivity's PTF-share perturbation used range [0,0.35] instead of the slider's actual [0.05,0.35], a minor overstatement of that one parameter's ±20% step.
Octave advancement is now FBS-gated (P(advance)=1−exp(−λ·FBS), a per-agent λ~U(0.001,0.008)) rather than a fixed probability — an agent at FBS=0 can no longer advance regardless of BLEI. Octave now governs CCO conversion-rate capacity (quality modulates the realized rate within it); previously octave had no role in conversion at all. PTF adoption gained a Bass (1969) imitation term based on current adoption share. External review (Refine.ink, Aug 2026) + BLEI paper §8 Table 6.
A documented share (25%) of PTH's annual housing-cost saving now routes into Acre Equity instead of landing entirely as liquid relief — the "payments build equity" mechanism was previously absent (appreciation-only); no new wealth is created, this re-routes where an already-counted saving lands. SZH's synergy coefficient θ is now threshold-gated (0 below 0.55, scaling to 0.25 at 0.90+) rather than plain-linear in coherence. v4.17 correction: the paper specifies this threshold on PTF merchant density; the code applies it to the SZH zone-coherence slider as a proxy, and realized PTF density never enters θ.
agentBLEI(), calcBLEIComponents(), the new FBS gate, and the Gini net-wealth adjustment now convert wage (SIU) to real USD via a single derived constant, SIU_TO_USD = (BASE_DAILY_COST×365)/SIM_COST_SCALE, before mixing it with dollar-valued terms — previously a raw SIU number was added directly into USD sums. agentEDC()'s saturation constants were rescaled so EDC responds to income above the median instead of sitting at a constant value for every realistic wage. The main wealth-accumulation loop was deliberately left unchanged — see Empirical Calibration below.
Baseline and CCO-only comparison populations are now generated independently from their own scenario parameters instead of being copied from the tested scenario's agents (which previously let e.g. the main run's Max Octave slider silently change the "baseline" comparison). Baseline's shock exposure is now paired with the main scenario's toggle (was hardcoded on) and its inflation anchored to the documented 3% CPI baseline (was copying the user's slider). "CCO Only" now mirrors the user's own CCO settings rather than a fixed reference calibration, isolating the marginal effect of PTF/PTH/SZH/CIP. When shocks are enabled, all three trajectories now draw from the same precomputed recession path ("paired shocks").
Fixed a flag-clearing bug that made every 3×/10×/50× Monte Carlo request silently execute exactly once with zero results collected — the MC controls were completely non-functional. Ablation/attribution now uses a paired population and paired RNG stream across every removal scenario plus a matched "full system" reference run, with signed deltas (a subsystem whose removal improves BLEI now shows as negative instead of being clamped to zero). OAT sensitivity now perturbs all five parameters by a uniform ±20% of each parameter's own slider range, replacing a mix of relative and fixed-percentage-point steps that didn't share a common scale.
Two validation tests silently tested nothing: the participation-threshold test passed {part:78}/{part:45} to a function that reads partRate, so both runs used the same 78%; the inflation test passed {inflRate:3}, read as a 0–1 fraction — 300%/yr, not 3%. Both fixed. The 10×/50× "Publication CI" label overstated what those bounds capture (a stochastic-error range at fixed parameters, not a full outcome-uncertainty interval) — relabeled "Stochastic CI" with copy stating the scope.
The p90/p10 wealth ratio only guarded against p10 ≤ 0, so a near-zero p10 produced a misleadingly large finite ratio instead of "∞" — threshold changed to <$100. Separately, OAT sensitivity's base-params setup mutated the object returned by getCurrentParams() directly; it now builds a fresh object instead.
The baseline and CCO-only comparison trajectories previously ran on the same shared RNG stream the main trajectory consumed next, so changing years or agent count shifted the main run's random sequence at a fixed seed. Each comparison run now uses its own seed-derived stream (mirroring the existing ablation RNG-swap pattern), restored before the main trajectory begins.
The DOMContentLoaded and load listeners both called applyPreset('reference'), silently re-applying all default parameters a second time before the first run executed. The load listener now only sets the reference seed/flag and calls runSim().
All 11 Chart.js canvases previously had no accessible name — a sighted-only presentation of every result. Added role="img" and a descriptive aria-label to each.
Added a collapsible acronym glossary above Scenario Presets, mirroring the Key Concepts accordion on the replication page, so first-time users don't have to leave the tool to look up CCO/PTF/PTH/SZH/CIP and related terms.
The slider was labeled "PTF market share" but only set the initial adoption probability at t=0; independent per-agent adoption checks in runYear() mean the realized final share can and does exceed the slider value. Relabeled to "Initial PTF share" in the UI, CSV export, and run-configuration summary, with a slider caption explaining the organic-growth behavior.
Added an ⓘ tooltip on the System Stability KPI disclosing that it is currently a trend+noise heuristic rather than a structural metric, with the exact formula documented in Known Limitations below.
Comparison and milestone tables carried a hardcoded min-width:640px, forcing horizontal scrolling on every mobile viewport regardless of actual content width. Removed the fixed minimum and added a small-viewport rule that shrinks font size and cell padding instead.
Previously the only way to stop a 50× Monte Carlo run once started was to refresh the page. Added a Cancel button, checked at the top of each simulated year and between chained Monte Carlo runs, that stops cleanly and restores the UI without a reload.
IS_REF_RUN was assigned inside the page-load listener without a prior declaration, creating an implicit global in non-strict mode. Declared explicitly alongside the other module-level state variables.
The "enable remaining systems" insight card rendered as "Enable SZH+CIPfor full integration" — a missing space before "for". Fixed.
Exported CSV/JSON files previously self-reported "v3.3" in headers, filenames, and metadata while the page displayed v3.4 — a version-string drift bug. Fixed by introducing a single META object as the source of truth for version, DOI, citation, and URL; all exports now read from it. Archival DOI (10.17605/OSF.IO/QWTE2) added to schema.org metadata, footer, References, and both export formats.
Internal key PRESETS.optimal renamed to PRESETS.reference throughout (definition, button id, PRESET_IDS, OAT sensitivity mini-runs). UI-facing labels ("Full Integration", "reference run") were already correct; this closes the gap between internal code and displayed terminology.
Added a live label under the Quality Multiplier Ceiling slider showing the Phi-adjusted effective maximum (e.g. "Effective max with Phi: 14.58×"), updating on slider drag and Phi toggle. Previously the 1.618× Phi bonus on top of the stated ceiling was undocumented in the UI.
initRNG(): now enforces the same 0–999999 range as the seed input's HTML min/max, not just a NaN check. agentEDC(): added a comment documenting that the 18/1100/820 constants are scenario-calibrated design targets (chosen so EDC lands within CFG.EDC_BASELINE_LO/HI at median wage for each tier), not derived from a closed-form model or external data.
v3.2: Gini computed on wealth clamped to ≥0 (WEALTH_FLOOR=−$10K broke standard formula). v3.3: totW=0 → Gini=0, not 1. (Old ||1 fallback incorrectly returned 1 for zero-wealth edge case.) Gini measures distribution shape; all agents at zero = perfect equality of deprivation. BLEI poverty (% <30d) captures the poverty level that Gini misses here.
Fix: Stability premium × 1/(1+0.5·max(0, wage/35−1)). Prevents structural compounding at high incomes. BLEI is purely measurement; the premium reflects the underlying stability it measures. Diminishing returns grounded in Mullainathan & Shafir (2013) + Carroll (1997): marginal benefit is largest at low-to-median wages.
Fix: Uniform distribution replaced by beta(5,2) over [0.70,0.95] — mild recessions most common, tail to 0.70 (30% loss, analogous to 2008–09). v4.0 correction: this entry previously stated the mode as "≈0.86 (14% income loss)", which is arithmetically wrong — beta(5,2)'s mode is (5−1)/(5+2−2)=0.80, giving a multiplier mode of 0.90 (10% loss). The distribution's mean (≈0.88, ~12% loss) and median (≈0.88, ~11.6% loss) are close to the cited NBER target and are unaffected; only the mode figure was wrong, and only in this prose, not in the beta(5,2) formula itself (external review, Refine.ink, Aug 2026, caught this across all three project pages). Calibrated against NBER post-WWII recession data: median US recession ≈ 12% peak income loss. Source: NBER Business Cycle Dating Committee.
NaN guards in agentBLEI, agentEDC, runYear, calcMetrics. p10/p90 now use proper linear interpolation (not index rounding). ANNUAL_BASE_COST renamed to SIM_COST_SCALE to avoid apparent contradiction with BASE_DAILY_COST ($68.33/day ≈ $24,941/yr vs 1,500 SIU simulation scale).
EDC: Switched from wealth/60 proxy to actual agent wage (consistent with BLEI v3.1 fix). lognormal: Math.max(1e-14,1−RNG()) protects against log(0)=−Infinity on seeds where RNG()=1. gamma(): 5,000 iteration cap prevents potential infinite loop.
v3.2: CCO comparison agents use deterministic index-based participation (not RNG()<0.78) — preserves seed reproducibility. v3.3: PRESETS now includes 'hiAI'; applyPreset() guards against undefined preset with console.error; all 5 preset buttons correctly highlighted.
BASE_DAILY_COST ($68.33/day, BLS CES 2023), SIM_COST_SCALE ($1,500, a calibrated ABM scale unit — NOT a dollar figure), WAGE_TO_USD (≈100.52, Census/BLS-sourced), and LIVING_WAGE_ANNUAL ($49,370) are four different anchors introduced at different points in this project's history, and it's easy to lose track of which one drives which formula today. An external review (Refine.ink, Aug 2026) originally flagged that wage (SIU) was being added directly into USD-typed sums in several places. Fixed in v4.0: agentBLEI, calcBLEIComponents, the FBS advancement gate, and the Gini net-wealth adjustment converted wage to USD via a derived constant, SIU_TO_USD = (BASE_DAILY_COST×365)/SIM_COST_SCALE ≈ 16.6 — these formulas don't compound over time, so a conversion constant there was safe. Deliberately NOT changed at v4.0: the main wealth-accumulation loop — an early attempt at a full fix proved dangerously sensitive to the conversion constant, since wage compounds against cost over up to 30 years (a ~3.5× range of candidate values swung 20-year outcomes from universal wealth-floor collapse to implausible multi-million-dollar median wealth). Fixed in v4.3: a validated anchor was found — WAGE_TO_USD (wage side) and LIVING_WAGE_ANNUAL (cost side), both passing all six VAL_TESTS including BU-monotonicity — and the main loop was rebuilt on it (see the wage/cost unit-fix entry further down this same panel for the full redistribution finding that came with it: this was not a "just bigger numbers" fix). Fixed in v4.4: BLEI/FBS/Gini's own wage-side conversion moved off SIU_TO_USD and onto the same WAGE_TO_USD anchor the main loop now uses, closing a ~6× gap between what an agent's wage was "worth" depending which formula asked. SIU_TO_USD is retired as of v4.4 and is not read by any function in this file today. SIM_COST_SCALE is likewise no longer load-bearing for wage conversion or the main loop; it survives in CFG purely as a historical constant, kept rather than deleted so old exported CSV/JSON files and this changelog's own worked examples from prior versions stay interpretable. Standard ABM calibrated-unit convention (Epstein & Axtell, 1996); base daily cost sourced from BLS CES 2023 (bls.gov/cex). (v4.12 note: this card described the pre-v4.3 state as current for three consecutive mechanics-changing releases — an external audit caught it, alongside the ODD panel's own equivalent staleness above. The main-loop fix and the SIU_TO_USD retirement were always correctly described in their own dedicated Cumulative Bug Fixes entries elsewhere in this panel; only this specific card, framed around "what v4.0 fixed," was never revisited when later releases changed the very thing it says was "deliberately not changed.")
| Quantity | Current value | Used for | Economic interpretation |
|---|---|---|---|
LIVING_WAGE_ANNUAL | $49,370/yr | Main wealth-accumulation loop (cost side) | Annual household living-cost anchor — a full national living-wage figure, appropriate for a 20-year compounding loop |
BASE_DAILY_COST | $68.33/day (≈$24,940/yr) | BLEI, FBS, PTH equity-contribution sizing | Bare-subsistence / poverty-line threshold — deliberately lower, appropriate for a "days covered" welfare metric |
v4.14: these are intentionally not the same concept — a household's ongoing cost of living (what has to be covered every year to avoid wealth decline) is not the same figure as the subsistence floor a welfare-poverty metric measures distance from. Added this table directly, per an external audit's request, so the distinction is visible to a reader without needing to reconstruct it from the surrounding prose.
BU → quality conversion does not create wealth ex nihilo. PTF businesses accept BUs and provide real goods/services (food, utilities) at cooperative prices. Quality multiplier reflects productivity gains distributed to workers via cooperative ownership (Mondragon model, Whyte & Whyte 1991). Conversion economy circulates within the PTF network. v4.0: external review correctly noted the engine credited conversion proceeds without debiting any treasury/production account. As a first step, cumulative conversion proceeds are now tracked and surfaced in CSV/JSON export (Conversion Ledger) for transparency. A genuine aggregate production constraint — one that could ration agent-level conversion when exhausted — is a further economic-modeling design decision (production function, capacity limits, failure behavior) that isn't implemented yet; see Known Limitations.
THREE separate PTH benefits as of v4.0: (1) housing cost reduction — a dimensionless 0.65 factor (cf*=0.65) applied to the main loop's LIVING_WAGE_ANNUAL-based cost, not SIM_COST_SCALE, which no function in this file reads (v4.15 fix: this card previously said "35% of SIM_COST_SCALE" — accurate at v4.0, superseded by v4.3's wealth-loop unit fix, and missed by the v4.12 staleness sweep that corrected the equivalent claim elsewhere in this file; caught by an external audit) — (2) a share (25%) of that cost saving routed into Acre Equity — the "payments build equity" mechanism the docs described but the engine previously didn't implement (it was appreciation-only) — and (3) Acre Equity appreciation, where a tenure-cohort share of the appreciation (15% at tenure=1, ramping to 85% at tenure≥5 — see pthLiquidShare() and the tenure-cohort haircut entry, above) is credited to liquid personal wealth, while the full appreciation is also added to acreEquity (illiquid) (v4.16 correction: the v4.15 rewrite of this card said the non-liquid remainder goes to acreEquity. The code adds all of it — a.acreEquity+=appr; a.wealth+=appr×share — so the liquid share is counted in both stocks and keeps compounding inside acreEquity. acreEquity itself is read by no metric, so every reported wealth figure excludes PTH equity entirely, members' own contributions included; the overlap's only observable effect is larger future liquid payouts. A value-conserving alternative is measured in CONTRIBUTING.md's v4.16 notes — at v4.16, seed 42 would have moved from $559,223 to $558,001 — and logged as a decision, not applied.) (v4.15 fix: this card previously described a flat "wealth += appreciation × 0.5," the pre-v4.5 haircut — superseded by v4.5's tenure-cohort ramp, above, and likewise missed at v4.12). The re-routed equity contribution doesn't reintroduce double-counting — it moves an already-counted saving, not new wealth. Consistent with Burlington CLT documentation (Champlain Housing Trust).
The framework is explicitly designed to approach universal prosperity for the coming automation wave, with BU as treasury-backed community infrastructure. Absence of housing scarcity and fiscal limits is a stated hypothesis — that cooperative institutions structurally reduce these frictions — not a modeling oversight. This is a documented divergence from traditional macro models.
Agents lack age, retirement, disability, household size, or mortality. Acceptable for structural scenario comparison; not suitable for policy forecasting requiring life-cycle dynamics. Future: age cohorts and household transitions (see CONTRIBUTING.md).
All social effects mediated by aggregate zone parameters (SZH) rather than explicit networks. A small-world network structure would better model PTF zone clustering and SZH coherence. Noted as a future development direction in CONTRIBUTING.md.
An external audit flagged that the baseline's name and its "real-world reference point" framing (now softened, see the preset tooltip) can read as a claim this scenario reproduces actual U.S. welfare institutions. It doesn't: pBase sets every architecture off and applies a flat 3% CPI, with no SNAP/EITC/TANF/SSI-SSDI benefit receipt, Medicaid, housing assistance, or unemployment insurance modeled as income to any agent. (The Social Security-Anchored Cohorts table tags agents by wage relative to real 2026 SSI/SSDI/retirement benefit levels for comparison purposes — it does not credit those benefits to baseline agents as income.) This scenario is best read as "what happens with none of these five architectures and no other offsetting mechanism," a deliberately clean no-intervention counterfactual — not as a calibrated stand-in for the current U.S. policy mix, which a rigorous comparison would need to model separately. Adding a genuinely calibrated current-policy benchmark alongside this counterfactual is a real, substantive future scenario, not attempted here; see CONTRIBUTING.md's Model Architecture Feedback.
Since v4.0 the Baseline comparison has been anchored to a fixed 3% CPI (CFG.BASELINE_CPI_RATE), while Your Settings and CCO Only use the inflation slider — 0% in Full Integration, CCO Only and High AI. Every vs-Baseline figure in those presets therefore mixes the policy effect with a 3-point inflation difference. The replication page motivates the 0% default as the framework's price-stability hypothesis for cooperative essential-goods provision; as implemented, that hypothesis is an assumed input rather than a model output — at 0% the engine's own PTF/PTH inflation damping has nothing to act on, and CCO Only, which has neither PTF nor PTH, runs at 0% too — so its assumed effect is counted inside the policy effect. Measured with harness.js infl-match (seeds 1–500, 500 agents, 20 years, shocks off): Baseline wealth poverty is 71.8% at 3% but 50.5% at 0%; its median wealth is pinned at WEALTH_FLOOR in 100% of runs at 3% but 0.6% at 0%; Full Integration's wealth poverty is 15.3% at 0% but 28.8% at 3%. So of the unmatched 56.5-point poverty gap, 35.2 points remain when both run at 0% and 43.0 when both run at 3% — about 24–38% of the headline gap is inflation, not policy. The long-documented finding that Baseline's median is "pinned at the floor in 100% of runs" is a product of this mismatch, not of the Baseline's policy mix alone. The new Match Baseline inflation to my settings toggle (Economic Environment) runs the Baseline at your slider's rate, isolating the policy effect; it changes the Baseline comparison only, never Your Settings' own trajectory, and is off by default so every documented figure is unchanged. A warning appears whenever the two rates differ and the toggle is off. Which behavior should be the default is a framework-level decision logged for Duke in CONTRIBUTING.md, not settled here.
Independent NEEC maintainers found this and it is confirmed here (v4.21: seeds 1–500 at 5,000 agents per run, 20 years; the 500-agent figures agree within their confidence intervals). Full Integration reduces wealth poverty by 78.7% and BLEI poverty by 82.3% against the shipped Baseline comparison. Those figures shrink under a fairer comparator: 69.7% / 74.5% with inflation matched at 0%, 56.5% / 61.1% with both scenarios at 3%. Against year 0 wealth poverty falls 59.6% (37.8% → 15.3%), while BLEI poverty rises (10.4% → 12.5%): it peaks near 22.6% around year 7 and returns to its year-0 level at year 24, so the 20-year horizon lands on its way down. No comparator gives 98%. Median wealth is $527,328, not $82,000, and depends mostly on agents saving all income above their basket (see that entry); TARGET_WEALTH, TARGET_POVERTY and TARGET_GINI are paper targets carried as constants, not model outputs. Reconciling the papers with the engine is a decision for the framework's author; the figures are regenerated by node harness.js headline.
The wealth loop subtracts only the agent's own discounted living-wage basket from income; nothing above it is consumed. Under Full Integration the population saves 44% of its income over 20 years, and the top wealth decile 75%, against a US personal saving rate of 3.4–5.6% in 2022–2025 (BEA). This produces most of the model's wealth levels, including the median of about $530,000 and the unbounded growth over longer horizons. It moves poverty much less: consuming 90% of the surplus, a 4% saving rate, cuts median wealth by three-quarters but raises wealth poverty only from 15.3% to 19.6%, and the reduction against the matched Baseline falls from 69.5% to 64.2%. node harness.js saving shows the sweep; a consumption rule is a pending decision (CONTRIBUTING.md v4.21).
Wealth poverty uses a fixed $25,000 in nominal dollars, and BLEI divides nominal resources by the year-0 daily cost ($68.33), so with inflation on both read slightly low. Measured, the effect is at most 1.5 points (Full Integration at 3%: BLEI poverty 27.5% against 29.0% at current prices; the 3% Baseline: wealth poverty 71.4% against 72.4%), because few agents sit near either line. The price index is also recomputed each year from that year's damped rate rather than accumulated, so realized annual inflation runs slightly below the damped rate as PTF adoption grows.
BLEI adds one month of BU's food value ($990 at the $1,200 reference) to an agent's resources and divides by the daily cost, which is $31.67 for CCO+PTH members: 31.3 days, above the 30-day poverty line whatever the member's wealth or wage. So no CCO+PTH member is counted BLEI-poor at a BU of $1,152 or more, even at the wealth floor. This follows the BLEI paper's definition, which counts a month of flows toward coverage. Under Full Integration, BLEI poverty therefore comes entirely from CCO participants outside PTH (8.9% of them at year 20) and non-participants (36.5% outside PTH, 11.1% in it).
The median year-0 wage income is $39,945 (exp(3.5) × 12 × WAGE_TO_USD) against a LIVING_WAGE_ANNUAL basket of $49,370, so about two-thirds of agents (66.6%) start in basket poverty. The Baseline has no transfers, so those agents run an annual deficit from year 1 and draw down their wealth; at 3% CPI, costs also outgrow wages (about 1–1.8%/yr). At 0% inflation the Baseline's wealth poverty plateaus near 50% and its basket poverty falls from 66.6% to 47.0%; at 3% wealth poverty keeps rising to 71.8% and basket poverty to 82.1%. The same gap produces a transient under Full Integration: BLEI poverty rises from year 0 to about 20% at year 5 before falling to 12.6%, while basket poverty falls steadily (66.6% → 40.6% at year 1 → 10.0%), because initial wealth is drawn independently of wage and deficit agents run down that stock. About 7.7 points of the apparent rise is measurement: at year 0, BLEI already credits participants' BU and cost relief (2.6% under Full Integration rules vs 10.3% for the same agents under Baseline rules). The Poverty by Five Measures card reports both year-0 figures and change against year 0 as well as against the Baseline. Whether to recalibrate the wage distribution or the cost anchor is a decision for the framework's author.
The engine models no housing tenure, mental illness or vocation, and at US scale (about 22 per 10,000) the measure is about one agent in a 500-agent run. It is therefore an expected share. Year 0 equals HUD's 2025 point-in-time rate by construction. The economic pathway (73%) moves with the share of agents whose income plus savings cannot cover the year's living-wage basket; the serious-mental-illness pathway (25%) changes only where PTH and SZH together provide wellness zones, by up to 50% scaled by zone coherence; the voluntary pathway (2%) is the same in every scenario. Three inputs are assumptions adopted by the framework's author and open to revision as data sources become available: the 2% voluntary share, the proportionality of economic homelessness to housing distress, and an SMI pathway that does not worsen with the economy. Under CCO Only the measure ends near its year-0 level: participants' housing distress falls, while non-participants inherit the Baseline's deterioration (see the living-wage basket entry). This definition differs from the World Bank's "extreme poverty," which counts income below $3.00 a day.
Through v4.18, CCO's cost relief was a flat 20% of the living-wage basket whenever BU was above zero, so the BU amount reached a household only through conversion proceeds. From v4.19 the relief scales with the BU issued: 20% at the $1,200 reference, proportionally above and below it, capped at 50% (CCO_RELIEF_CAP, a placeholder with no source; it binds from $3,000 of effective BU). Every $1,200 preset, including the seed-42 regression, is unchanged; Stress Test ($900) and any run at another BU amount move. Conversion proceeds are still credited once per simulated year, although wages are annualized ×12; crediting twelve allocations a year was measured and not adopted, because it would scale the unconstrained conversion channel (see the stock-flow entry) twelvefold and move seed 42 to 9.2% wealth poverty and $1.19M median wealth. The 20% at $1,200 is carried over unchanged from earlier releases.
Inflation is a fixed input rate, damped only by realized PTF and PTH membership; no price responds to demand, BU issuance, conversion volume, recession or automation. Recession scales income only, and automation slows wage growth only. An endogenous price channel would be needed before the model could test claims that the framework is non-inflationary, rather than assume them.
55% CCO participation: the papers' minimum-viable level appears here only as a run warning. No line of runYear() reads aggregate participation, and a participation sweep shows no break at 55% (wealth poverty 23.2% at 45%, 20.9% at 55%, 18.4% at 65%). SZH synergy θ: the paper gates θ on PTF merchant density; the code gates it on the SZH zone-coherence slider, and realized PTF density never enters. Building either as a dynamic would change the model and is logged as a decision.
Both mechanisms are real and calibrated, but each implements a reduced-form slice of what its name evokes, not the full phenomenon — worth stating plainly rather than leaving a reader to infer the scope. Recession scales two income flows and nothing else: earned wage income and CCO conversion proceeds, both multiplied by incomeShock (which also carries every agent's ordinary ±10% annual wage-variance draw, recession or not) (v4.16 correction: this entry said recession "only ever multiplies earned wage income" — convGain is multiplied by the same incomeShock). It does not touch asset prices, employment status, housing costs, business activity, government transfers, or interest rates. "Exogenous labor-income shock" is a more precise description than "recession model." AI automation subtracts a displacement term directly from wage GROWTH (popAIDisp × automationRisk) — there is no explicit job-loss event, job search, unemployment spell, or re-employment transition; an agent's income slows, it never actually disappears and returns. Both are legitimate, common first-pass ABM simplifications (and each is already flagged individually elsewhere in this panel — see "No demographic structure" and the wage-floor entry, further up), but the specific claim "this is a recession/automation model" is easy to over-read as more general than either mechanism actually is. A fuller macro-shock model (asset prices, employment transitions, transfers) and an occupation-based re-employment model for AI are both real future-work candidates — see CONTRIBUTING.md's Model Architecture Feedback.
Through v4.2, automationRisk ∈ [0.2, 1.0] was drawn from a uniform distribution — real automation exposure is highly bimodal and occupation-dependent (Frey & Osborne, 2013; Autor, 2015), and the uniform assumption understated polarization in High Automation scenarios. v4.3 made it a Beta(6,1)/Beta(1,6) mixture weighted 47%/53%; v4.20 re-weights it 63%/37%, which matches the employment-weighted mean probability (0.592) of Frey & Osborne's 702 occupations — see Cumulative Bug Fixes, above. What remains: risk is drawn independently of an agent's wage, although across occupations log median wage and automation probability correlate at −0.65 (employment-weighted), so the model spreads automation's wage drag evenly across the wage distribution where the data concentrate it among lower earners. Occupation-stratified risk, tied to wage, remains a further-refinement item in CONTRIBUTING.md.
The metric is the inverse coefficient of variation over the run's final quarter. At the slider's 5-year minimum that quarter is barely two years, and before v4.13 it was rounded down to a single point, which made the score a constant 0.99 for every 5–7 year run (see Cumulative Bug Fixes). The window floor is now 2, which is enough to compute a real variance but is still a very small sample: treat System Stability on any run shorter than ~8–10 years as indicative at best, and prefer a 20-year run (the reference configuration) when the number matters. This is a limitation of the window, on top of the separate, already-documented limitation of the CV formula itself (below), which cannot tell a thriving equilibrium from a population pinned at a floor.
The two Cumulative Poverty Exposure KPIs sum each year's population poverty share, which gives the mean years-poor per agent exactly — but says nothing about how those years are distributed across agents. "Every agent poor for 3 years" and "30% of agents poor for 10 years" produce the same number, and they are very different policy situations. Distinguishing them needs per-agent poverty spells tracked over time, which this engine does not currently keep (agents carry no poverty history, only current state). The related metrics the v4.12 audit also suggested — time-to-tier-attainment per agent, wealth-floor recovery rates, spell-length distributions — all need that same longitudinal layer and remain open; see CONTRIBUTING.md. Note also that the wealth-poverty figure inherits POVERTY_LINE's own undocumented provenance (see Cumulative Bug Fixes and the note at that constant), so it is best read comparatively — Your Settings against Baseline — rather than as an absolute count.
OAT sensitivity varies one parameter at a time ±20% of its own slider range (v4.0: uniform across all five parameters — see Cumulative Bug Fixes) and records the poverty and BLEI response from actual model runs. It captures first-order single-parameter effects only. Parameter interactions and higher-order effects are not quantified. For rigorous uncertainty quantification, use the CSV export with external Sobol analysis in Python/R (see CONTRIBUTING.md).
Historical note: through v4.1 the displayed value was a heuristic, stabBase = improving ? 0.88 : 0.60 plus RNG()×0.08 noise — not derived from wealth variance, BLEI volatility, or agent-state transitions (v3.6 flag). Resolved in v4.2: structuralStability() is the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter — no RNG call, no manual per-mechanism bonus. This formula has its own honest limitation, not previously disclosed here: a population with genuinely flat, unchanging outcomes scores as maximally stable, whether that flatness reflects a thriving equilibrium or a population pinned at a floor with nowhere left to fall — coefficient of variation can't distinguish the two. In the v4.4 N=5,000 study, the Traditional Welfare Baseline — median wealth pinned exactly at WEALTH_FLOOR in 100% of runs by year 20 — scores 99.0% stability, higher than Full Integration's 88.6%, precisely because a hard floor has near-zero variance left to measure. Read this metric as trajectory steadiness, not as an endorsement of the outcome it describes, and always alongside the poverty and BLEI figures next to it, never in isolation. Full N=5,000 comparison in CONTRIBUTING.md.
v4.0 fixed wage/USD unit mixing in BLEI, FBS, and Gini but deliberately left the main runYear() wealth-accumulation step on its pre-v4.0 SIU-scale convention, because a full fix proved dangerously sensitive to the conversion constant. v4.3 ships a validated fix (WAGE_TO_USD/LIVING_WAGE_ANNUAL — see Cumulative Bug Fixes, above) after finding an anchor that passes all six VAL_TESTS. This resolves the unit-simplification item but opens two new ones, not yet resolved: whether WEALTH_FLOOR = −$10,000 is still an appropriate floor now that the cost anchor it's measured against roughly doubled (median Baseline wealth now sits exactly at that floor, not just an occasional insolvency case), and whether TARGET_WEALTH/TARGET_POVERTY/TARGET_GINI — set against the old loop — need their own reconsideration against the structurally different new baseline. Both flagged in CONTRIBUTING.md as framework-level decisions, not resolved here — v4.4 adds an in-app pointer to this caveat directly on the affected KPI badges (Gini, Wealth, Stability, Flourishing, EDC — the ⓘ icons), but does not pick new target values. v4.8: a validated N=500-seed sweep across four candidate WEALTH_FLOOR values now exists (see CONTRIBUTING.md) — headline finding: Traditional Welfare Baseline is completely invariant to the floor's exact value on every metric except the raw wealth number itself, while Full Integration shows a real but modest sensitivity (Gini and poverty rate both move a few points as the floor deepens) via agents who dip negative and later recover spending longer at BLEI-liquid-zero the deeper they're allowed to fall first. This is data for a decision, not a decision — the shipped default is unchanged. (Separately: BLEI/FBS/Gini's own wage-to-USD conversion, left on the older SIU_TO_USD approximation since v4.0, is unified onto WAGE_TO_USD in v4.4 — see Cumulative Bug Fixes, above. That's a different unit issue from the one this entry covers.)
The new Social Security-anchored cohort study (Cumulative Bug Fixes, above) found that Full Integration does not lift a low-wage cohort out of poverty by the simulation's own tier definitions, even though every existing mechanism already applies to it equally — it helps this population enormously in relative terms (originally measured at 20–30× the baseline's BLEI; recomputed at ~7–10× under v4.4's wage-scale unification, since Baseline's own BLEI moved too — see that entry) without coming close in absolute terms. Whether this points to a genuine mechanism gap (e.g. wage-conditional BU scaling) or accurately reflects the framework's intended scope is an open question for the framework's authors, not resolved here — see CONTRIBUTING.md.
BU → wealth conversion proceeds are credited to agents without debiting a modeled treasury, PTF balance sheet, or production account — external review correctly flagged this as an open stock-flow accounting gap. v4.0 adds transparency only: cumulative conversion proceeds are tracked and exported (CSV/JSON "Conversion Ledger"). A genuine aggregate production constraint — with a defined production function, capacity limits, and rationing behavior when exhausted — is a further economic-modeling design decision not implemented here; see CONTRIBUTING.md.
Parameters calibrated against analogues (Alaska PFD, CLT network, Mondragon) but outcomes not validated against deployment data. Phase 4 roadmap: validation against Fed SCF, CPS, ACS, BLS CES, and OECD inequality trajectories. Required before claims of predictive validity.
Agent wealth is drawn lognormal(CFG.WEALTH_INIT_MU,CFG.WEALTH_INIT_SIGMA) = lognormal(10.5, 1.2), whose median is exactly e^10.5 ≈ $36K by construction, matching this file's own long-standing "Fed SCF 2022" citation. The actual 2022 Survey of Consumer Finances reports median household net worth at ≈$193K (Kuhn & Ríos-Rull, NBER working paper, 2025, using 2022 SCF microdata) — roughly 5.3x higher, and not close enough to be plausibly explained by survey-vintage drift alone. This is independent of the wealth-init distribution's already-documented inherent Gini ≈0.60 (a function of WEALTH_INIT_SIGMA alone — Gini of a lognormal is 2Φ(σ/√2)−1, with no μ term — so correcting the median would not move that figure); it mainly affects absolute wealth-poverty rates in early simulated years, since POVERTY_LINE is a fixed dollar amount. One caveat worth stating plainly: it's possible $36K was meant to anchor a narrower concept than total net worth (e.g. liquid financial assets) rather than being a plain miscalibration — but no comment anywhere in this file or CONTRIBUTING.md says so, so as written this reads as an uncorrected citation mismatch, not a documented scope choice. Logged as an open calibration: item in CONTRIBUTING.md's Model Architecture Feedback; not changed here — see the note at CFG.WEALTH_INIT_MU/SIGMA, above, for why a fix needs the same mechanics-audit-plus-large-N-restudy treatment as v4.0/v4.3/v4.4 rather than a quiet value swap.
Version History (v3.1 → v5.6.0)#
What changed in each release and why, newest first. Open a release to read it in full.
Version highlights, v4.0 → v5.6.0
runYear() itself that v4.12's pass had fixed only in the surrounding panels, and added two reporting features — Cumulative Poverty Exposure (mean years an agent spends below each poverty line across the whole run) and export of the v4.12 dominance check. No simulation-mechanics changes. v4.12: a documentation-and-verification pass prompted by an external (Perplexity) audit, plus a dominance-check addition to the Threshold Sensitivity charts — corrected an in-app documentation error on the v4.6 PTF adoption cap (23.8% → the correct, already-CONTRIBUTING.md-documented 19.0%, settled via a fresh Node.js harness reproduction), fixed three places where the ODD/Empirical Calibration panels still described pre-v4.3/pre-v4.4 mechanics as current, and softened the Traditional Welfare Baseline's framing so it isn't mistaken for a calibrated model of actual transfer programs. Zero simulation-mechanics changes; the new dominance check is a pure reporting transform, validated against 9 constructed cases before running against real seed-42 data. v4.11: added a poverty-gap ("avg. shortfall") view to the v4.10 Threshold Sensitivity charts — a toggle switches both charts between the existing % of population below a threshold (headcount ratio) and a new view showing the average per-capita shortfall in the same dollars/days as the axis (poverty gap ratio), prompted by an external methodology review of the v4.10 feature that reconfirmed its Atkinson (1987) citation and recommended exactly this pairing (Foster, Greer & Thorbecke 1984). Zero simulation-mechanics changes, validated against a naive reference across 22,210 random trials plus a full end-to-end integration test against the engine's own real simulated output. v4.10: added Poverty & Wealth Threshold Sensitivity charts — rather than one committed poverty-line dollar figure or BLEI-day cutoff, two new charts show the full cumulative distribution across a range of thresholds at once (the "poverty dominance" approach: Atkinson 1987; the World Bank's practice of reporting multiple poverty lines), Baseline/CCO-Only/Your Settings overlaid, plus one externally-verified reference point (2026 HHS federal poverty guideline, $15,960/1-person). Purely a reporting transform of already-simulated values — zero simulation-mechanics changes, confirmed via a full end-to-end Node.js integration test cross-checking the new function against the engine's own existing figures at matching thresholds (exact match). v4.9: fixed a table-overflow bug (Social Security-Anchored Cohorts table clipped at 100% zoom, same fix pattern as the System comparison table's own scrollbar), verified a Meta AI audit's contributions against source rather than accepting them at face value — a modular harness rewrite checked out and reproduces the documented regression exactly, a proposed LIVING_WAGE_ANNUAL cut did not (it rested on a stale 2024 MIT snapshot; current MIT data runs meaningfully higher across every state checked, so the change wasn't made), and the archived DOI snapshot's OSF metadata has been updated to reflect the Apache 2.0/CC BY 4.0 split; v4.8: UI fixes (tooltip overflow, chart-grid overflow at non-100% zoom, dual-axis fix for the near-invisible benefitDays line), code relicensed to Apache License 2.0 (content/papers remain CC BY 4.0), a validated WEALTH_FLOOR sensitivity sweep, occupation-risk dataset lead checked further; v4.7: dependency/documentation hygiene — pinned-and-verified Chart.js SRI, LHS export scatter preview, PTF terminology clarification, wealth-init/SCF calibration finding logged; v4.6: Latin Hypercube sensitivity export, opt-in feedback-clamped PTF adoption cap, FBS₅₀ half-saturation constant, 40-year near-poverty cohort rebuild — good-first-issues pass; v4.5: tenure-cohort PTH liquidity haircut, shareable parameter URLs, Social Security-anchored cohorts surfaced as in-app UI, tier/preset hover tooltips; v4.4: unified wage/income scale across BLEI/FBS/Gini, true common-random-numbers pairing across all three comparison scenarios, corrected RNG sampler bug; v4.3: main wealth-loop unit fix, Social-Security-anchored cohort study — external audit; v4.2: common-random-numbers population pairing, structural System Stability metric, 6-check validation suite — external audit; v4.1: ablation attribution, UI corrections; v4.0: Mechanism Alignment & Dimensional Coherence — external Refine.ink review: FBS-gated advancement, unit-consistent BLEI/FBS/Gini, paired-population comparisons, Monte Carlo/ablation/OAT/validation fixes. Full detail on every release in Version History, below.The earlier engine’s headline figures (v4.22, as coded; the v4.4 large-N study design: 500 seeds of 5,000 agents). These were shown at the top of this page until v5.2.1 under a v5.2 heading; they are not the release results.
Earlier engine, Full Integration after 20 years
Full Integration after 20 years; 500 seeds of 5,000 agents. A design reference point under the reference parameters, not a forecast.
Release highlights
v5.6.0Oct 2026Today’s safety net (SNAP with its work rule, the EITC and the refundable Child Tax Credit) in both runs.
v5.5.0Oct 2026The 500-seed health and sensitivity study on its own page; no engine or figure change.
v5.4.1Oct 2026How to read the wealth result; words only, no figure, page result or engine change.
v5.4Oct 2026Labor and markets: job loss and re-employment, community housing’s books, community businesses by sector, risk measures.
v5.3.1Oct 2026v5.3’s figures, re-released at the complete commit; no figure, page or engine change.
v5.3 tag and its reproduction archive hold an intermediate tree; the live page was never affected. Tags are never moved, so v5.3.1 re-releases the same figures at the complete commit, and the workflow now tags the merge commit on main.
v5.3Oct 2026households and children: the adults live in households with their children, the Hub’s full list of what BU buy, survey wealth and spending that follows income; what each part of the design does is measured.
v5.2.2Oct 2026audit fixes: corrected wording, retired claims kept off the pages, the wealth line read from the code, a model specification and reproduction guide; every figure unchanged from v5.2.
v5.2.1Oct 2026presentation only: the findings explorer, guided sections on the simulation page, key concepts at the top of this page; every figure unchanged from v5.2.
data/releases/, listed in data/manifest.json), written by dev/tools/release_data.py; a check confirms that each figure shown equals the data. The findings that sat at the top of this page moved to the findings explorer; their old links lead there. Notes: CHANGELOG.md, “v5.2.1 Release Notes”.v5.2Oct 2026the model round: savings that keep up with prices, aging, the no-program run against US data, a middle backing reading and robustness readings, each beside an unchanged main result.
v5.1Oct 2026audit fixes: the wage-bonus timing, the price level as a typical run with a range, the Hub’s targets beside the results, the backing-share curve.
v5.0.1Oct 2026the external audit pass on v5.0, applied as a pull request; text, tests and three page fixes, every figure unchanged; the 40-year horizon.
v5.0Oct 2026the release engine: Compassionism as specified on the Hub, every mechanism in, against no program, in three environments.
v4.22Sep 2026the open policy testbed reaches the page: Compassionism compared with five other anti-poverty designs under the same rules.
harness.js, not run live, and it uses the testbed's accounting profile, which differs from the live scenarios in ways its caption lists. The engine is unchanged: the seed-42 regression and every preset's figures are bit-identical, so the headline figures above still hold. Also: tooltips are no longer cut off inside the comparison table, and the page no longer scrolls sideways on phones.
v4.21Sep 2026an investigation of the v4.19–v4.20 figures that read counter-intuitively, one bug fix, and a large-N study.
domtest.js now 82 checks.
v4.20Sep 2026three external audits of v4.19 (Grok, Claude Sonnet and ChatGPT) checked claim by claim, plus an internal audit; the framework's author signed off on…
harness.js validate now fails on a mismatch, and GitHub Actions runs all of it on every push. domtest.js now 78 checks; the first CI run exposed a timing race in its own test setup, dating from v4.13, now fixed. The Automatic Stabilizers panel's labels and hints are reworded, with COLA spelled out as cost-of-living adjustment. The automatic stabilizers' shock-neutral defaults (×1.35; ×1.50 under Stress Test) were rechecked and stand.
v4.19Sep 2026requested by the framework's author.
v4.18Sep 2026requested by the framework's author.
domtest.js now 56 checks. The seed-42 regression is unchanged (1,965d · $559,223 · 0.534 · 16.6%).
v4.17Sep 2026an internal audit of v4.16, plus notes from the independent NEEC maintainers (run against pinned v4.15) and a Claude Sonnet audit of v4.15, each…
META.VERSION; BU Expiry added to the CSV; the LHS design's PTF column is live when the page toggle is off. Additions. An Adverse Environment preset (reference settings in the Stress Test's environment); recessions and four new modes in harness.js; domtest.js now 48 checks. The 55% participation threshold (not a dynamic) and θ's gate (the coherence slider, not PTF density) are relabeled. The seed-42 regression is unchanged (1,965d · $559,223 · 0.534 · 16.6%).
v4.16Sep 2026an internal audit of v4.15, every finding confirmed by direct computation before acting.
runYear()'s annual schedule enumerated line by line (the ODD panel gains its missing Process Overview & Scheduling element); the validation suite's benefit check correctly paired and its invariants check extended (determinism, exactly eight RNG draws per agent-year, balance and λ bounds, PTF cap); three documentation claims corrected against the code, including PTH appreciation accounting, whose value-conserving alternative is measured and logged as a second decision. Seed-42 regression unchanged; domtest.js now runs 38 checks, nine new, each verified to fail against v4.15. Full detail in CHANGELOG.md's v4.16 Release Notes.
v4.15Sep 2026two independent external audits of v4.14 — MuseAI (a focused code-level audit that ran this project's own harness.js and domtest.js before reporting,…
harness.js and domtest.js before reporting, a first for an external audit here) and Grok (a broad clarity-and-rigor review that deliberately avoided re-flagging already-logged items, and independently found no additional mechanics bugs on the default or documented paths). Every claim was checked against source and by direct computation before anything was acted on. One more genuine mechanics bug, found directly beneath the v4.14 PTF fix: PTH's inflation damping was a flat, toggle-triggered 10% reduction (inflRate*=0.90) that referenced no adoption-scale quantity at all — a 5%-uptake configuration got exactly the same population-wide damping as a 50% one, and PTH switched on with zero members still damped inflation. It is now scaled by the population's realized PTH share (the same fix shape as v4.14's PTF repair; the 0.10 coefficient is unchanged and becomes the ceiling at 100% membership). Proven inert on every documented figure by direct comparison, not argued: all five shipped presets were run under the old and new engines and every agent's final wealth compared — four of five are bit-for-bit identical (including hiAI with AI automation on); only Stress Test moves, the one preset combining PTH with nonzero inflation. The seed-42/Full Integration/20yr regression is identical, so none of the headline figures on this page changed. The direction is the opposite of the v4.14 PTF fix: at a realistic 10–20% uptake the flat factor overstated the damping, so the fix weakens it (harness-computed Stress Test, shocks and automation off: poverty 41.8%→43.2%, median wealth $158,155→$143,494). Four smaller items: the BU-expiry decay is guarded against NaN if the expiry parameter were ever missing (never a live defect — but the failure would have silently zeroed every CCO participant's wealth each year); the Monte Carlo 95% CI now uses Student's t instead of a fixed z=1.96 (at n=10 the old interval was about 13% too narrow and covered only ~91.8%); a fourth stale PTH documentation card that v4.12's staleness sweep missed is corrected; and this page's own header “Version highlights” toggle, whose 700px height cap its content already exceeded, no longer clips its oldest lines. domtest.js gained four regression-guard checks (29 total), each verified to fail against an unmodified v4.14 page. Full notes — including a sweep of every other population-level setting in runYear() for the same defect class (none found) and the errors caught in this release's own handoff documents — in CHANGELOG.md's v4.15 Release Notes.
v4.14Sep 2026a two-pass external ChatGPT audit — a general review, then a second pass focused exclusively on the economic mathematics and causal structure of…
runYear(). That second pass found what seven prior audits and this project's own six-check validation suite had all missed: two genuine mechanics bugs, not documentation drift. (1) The "BU expiry (months)" slider spans 1–6 in the UI but the code (decay=p.expiry<2?0:0.7) only ever implements two behaviors — expiry=1, and everything else identically. Confirmed directly: expiry=3 and expiry=6 produced bit-identical output before the fix. Replaced with a continuous decay=1-1/expiry. (2) PTF's inflation-damping term used the static initial-adoption slider where it should have used the population's actual current membership — a quantity this exact function already computed, three lines later, for an unrelated purpose, and simply never reused. Fixed by reordering and reusing it. Both fixes are proven to have zero effect on every documented figure in this project — the seed-42/Full Integration/20yr regression is bit-identical, confirmed three independent ways, so none of the headline KPI figures on this page needed to change this release. Effect size where the fixes DO apply (Stress Test preset, and the one validation check that exercises the affected line) is measured and reported in full in CONTRIBUTING.md. Also this release: a new in-app Wealth Floor Diagnostic KPI (share of the population pinned at the wealth floor, a same-run companion to the existing offline sensitivity sweep); EDC-adjusted Gini reframed explicitly as a constructed stock-minus-flow inequality index rather than conventional net worth; a dual-cost-anchor clarification table; and explicit documentation, in the ODD panel and inline in the engine itself, of the CCO/PTH feedback loops through FBS and octave advancement that the audit's causal-structure review identified — a headline "CCO effect" is a compound of several coupled mechanisms, not one isolated channel. Several of the audit's other findings restated substantial existing documentation (the stock-flow accounting gap, FBS λ calibration status, the already-explicit ODD Level 1–5 validation hierarchy) or proposed large architectural changes (a full flow-of-funds ledger, monthly BU tranches, causal pathway decomposition, source modularization — the last already rejected once before, at v4.7, for the same reason) logged for future consideration rather than attempted this pass. Full notes in CHANGELOG.md's v4.14 Release Notes.
v4.13Sep 2026an independent audit pass, and the first release in this project's history to find defects by running the interactive tool rather than reading it.
runYear() itself the whole time (claims that SIM_COST_SCALE is still load-bearing, that Yusd uses the retired SIU_TO_USD, that FBS "doesn't feed back into wealth" when v4.4's own notes explain that it does). All corrected. Two reporting additions: Cumulative Poverty Exposure — the mean years an agent spends below each poverty line across the whole run, answering the "final-year snapshot only" gap the v4.12 audit raised (at the reference configuration the final-year wealth-poverty rate is 16.6%, but the average agent spends 4.8 of 20 years below that line, against the Baseline's 11.8) — and the v4.12 dominance check, previously screen-only, now exported to CSV/JSON with its view and grid resolution, plus an on-page caveat that it is checked at plotted thresholds only and describes one stochastic run rather than a confidence interval. No simulation-mechanics changes. Full notes in CHANGELOG.md's v4.13 Release Notes.
v4.12Sep 2026a documentation-and-verification pass prompted by a Perplexity AI audit of the shipped simulation, plus one small feature addition — no…
v4.11Sep 2026one small feature addition on top of v4.10, prompted by relaying that release's own design to an external AI system (Grok) for a methodology critique…
WEALTH_FLOOR was tracked to the cross-check's own coarse resolution near a sharp discontinuity (not a flaw in the shipped function), confirmed by an exact-matching direct sum and by showing the discrepancy shrink to near-zero as the cross-check's own resolution was refined. Separately, the same external system's data-gathering follow-up on two long-open items produced a mixed but useful result: its 51-state, Census-weighted LIVING_WAGE_ANNUAL recomputation ($24.62/hr population-weighted, versus the shipped $23.735/hr unweighted) had its state-level inputs spot-checked and confirmed against live MIT data this session, though the full weighted arithmetic wasn't independently re-derived to the dollar — documented as reference data, not adopted into the engine, per the same "sweep, don't decide" standard as the WEALTH_FLOOR sweep. Its claim about the occupation-risk Kaggle dataset's license (unspecified, not MIT as an earlier session's third-party source suggested) could be neither confirmed nor refuted directly, but the search for it surfaced a materially better-documented alternative: a 702-row Frey & Osborne-matching dataset in the plotly/datasets GitHub repository, under a genuine first-party MIT license. Full notes in CHANGELOG.md's v4.11 Release Notes.
v4.10Sep 2026one new reporting feature, prompted directly by the framework's author — no simulation-mechanics changes.
LIVING_WAGE_ANNUAL was set in v4.3 and an external audit tried to overturn it in v4.9 with a different single figure that turned out to be stale. Rather than resolve that pattern by defending one more number, two new charts show the full range instead: the population's cumulative distribution of final-year wealth and BLEI across every plausible threshold at once, not just one committed figure — the standard "poverty dominance" approach from the poverty-measurement literature (Atkinson, A.B. (1987). On the measurement of poverty. Econometrica, 55(4), 749-764; the World Bank's own practice of reporting global poverty at several lines simultaneously). Baseline, CCO-Only, and Your Settings are overlaid on the same axes, so a reader can see whether one scenario dominates another across the whole range, not just at a single arbitrarily chosen line. One external reference point was added for comparison — the 2026 HHS federal poverty guideline for a one-person household, $15,960/yr — verified directly this session after a first fetch attempt returned a stale cached 2017 snapshot of the source page (caught and cross-checked against an independently-dated primary-source PDF instead of used). Deliberately out of scope: LIVING_WAGE_ANNUAL itself is genuinely baked into the wealth-accumulation loop (unlike POVERTY_LINE, a pure reporting threshold with exactly one call site), so making it explorable the same way needs its own dedicated, fully-audited mechanics release — scoped as the clear next step, not attempted here. Validated well beyond a syntax check: a full end-to-end Node.js integration test evaluates the actual edited script and cross-checks the new function's output against the engine's own existing poverty figures at matching thresholds — exact match. Full notes further down this same panel (v4.10 entry) and in CHANGELOG.md's v4.10 Release Notes.
v4.9Sep 2026a UI fix plus a full verification pass on an external AI audit — no simulation-output changes.
<table> sitting in a card with no local overflow handling, unlike the System comparison table's own scrollable container; fixed the same way, with a small reusable CSS class. External audit, verified rather than trusted: a Meta AI review supplied two contributions — a modular (ES-module) rewrite of the v4.8 Node.js harness, and a proposed cut to LIVING_WAGE_ANNUAL from $49,370 to $35,021. Both were actually run, not just read. The harness rewrite checked out, reproducing the documented seed-42 regression exactly. The living-wage proposal did not: it was built from a 2024 snapshot of MIT's Living Wage Calculator, and fetching MIT's own site directly for its current (Feb 2026) data found every spot-checked state running well above that snapshot — California $30.48/hr, DC $26.72/hr, even low-cost Mississippi at $20.69/hr, all above the 2024 figures the proposal relied on. The cut was not made. Also resolved: the archived DOI snapshot's OSF metadata, flagged as an open loose end since v4.8, has been updated by the author to reflect the Apache 2.0/CC BY 4.0 split. Full notes further down this same panel (v4.9 entry) and in CHANGELOG.md's v4.9 Release Notes.
v4.8Sep 2026a mixed release — five UI fixes from Duke directly using the deployed page (not a code audit), a licensing decision, and completion of two items…
minmax(0,1fr), applied throughout, including a second silently-overriding duplicate rule found in the process); the BLEI-components chart's near-invisible benefitDays line, confirmed via a fresh harness to be a chart-scale issue and not a computation bug (cashDays legitimately runs ~100× larger by year 20), fixed with a dual-axis chart; and confirmation that Baseline scoring higher System Stability than Full Integration is the already-documented coefficient-of-variation behavior, not a new bug. Licensing: the simulation's source code moves from CC BY 4.0 to the Apache License 2.0 — both v4.7 external reviews flagged CC BY 4.0 as unconventional for software, this had been logged as open, and Duke authorized the change this session; papers and documentation, including this page, remain CC BY 4.0. Sensitivity sweep: a validated N=500-seed WEALTH_FLOOR sweep (built and checked against the exact seed-42 regression figures before being trusted) found the Traditional Welfare Baseline completely invariant to the floor's value on every metric but the raw wealth number, while Full Integration shows a real, modest, mechanistically-understood sensitivity — not acted on; the shipped default is unchanged. Occupation-risk dataset: stronger, still-incomplete corroboration of the Kaggle automation-risk dataset flagged in v4.7. Full notes further down this same panel (v4.8 entry) and in CHANGELOG.md's v4.8 Release Notes.
v4.7Sep 2026a dependency-and-documentation hygiene pass — no simulation output changes anywhere in this release.
v4.6Aug 2026another good-first-issues pass, not a mechanics-audit release.
v4.5Aug 2026a good-first-issues pass, not a mechanics-audit release like v4.3/v4.4 — implemented the tenure-cohort PTH liquidity haircut (replacing the flat 50%…
v4.4Aug 2026unified the wage/income scale across BLEI, FBS, and Gini onto the same real-dollar anchor the main wealth loop already used since v4.3 (previously…
v4.3Aug 2026the main wealth-accumulation loop's unit fix (wage and cost sides both converted to real USD via sourced anchors — see the detailed entries below),…
v4.2Aug 2026common-random-numbers population pairing and a structural System Stability metric (see Methodology Overview below).
v4.1Aug 2026audit-driven bug-fix release — no simulation mechanics changed, so every figure above is reconfirmed rather than newly computed, except the last tile…
Detailed revision notes
Full revision history (v3.1 → v4.22 — strictly chronological through v4.6, then newest-first for v4.7 onward; see note below) Shown newest first; through v4.21 the older notes ran v3.1 to v4.6 and then v4.21 back to v4.7.
v4.22Sep 2026the policy testbed and the front door; tooltips; design sources (Sep 2026)
harness.js, harness-only and behind switches that leave the engine bit-identical: an issuance ledger of where program money enters, moves and leaves (node harness.js ledger); a money-creation and price module with a breakeven additionality and a price-neutral point (price); a labor-supply module with one income effect per unconditional dollar (ρ 0.16, from Vivalt et al., NBER w32719) and one compensated elasticity (0.33) (labor); comparator presets for a basic income, a negative income tax, an asset endowment, a public grocery network and X-Cents, matched to Compassionism's cost where relevant, under one cost ledger and a tax, money or hybrid financing switch (testbed … match / frontier / alts / decomp); and a reporting upgrade with poverty depth and persistence, cost per point removed, the price path, hours, the disposable-income Gini and group losses in dollars (testbed … a5). Its profile: the three text-versus-engine corrections (θ on realized PTF density, PTH cuts housing only, discounts skip the basket's tax share), wages indexed to prices, the design-neutral BLEI gate, and, from v4.22, the BLEI-gated raise counted as the program's where the design's own support carries an adult over the gate (N7_BLEI). (2) The front door: a band above the layout with the question, the comparison (written into the page by node harness.js frontdoor, naming its engine version, seeds and reproducing command), the six presets as one-click runs, one caveats box that stays visible, and method links. Cross-design comparisons lead with poverty severity (FGT₂) averaged over all 20 years, with year 20 beside it, because year 20 alone overstated Compassionism's gains. (3) What each design is: an entry per design with its source (NBER w32719; Moffitt 2003; Ackerman and Alstott 1999 and Hamilton and Darity 2010; the NYC Mayor's Office, July 27, 2026; the CurrentSea X-Change paper). (4) Tooltips: one floating box placed by script replaces the CSS ::after boxes, which the comparison table's scroll box clipped and which widened the page to 421 px on a 390 px phone. (5) This page moved into the simulation's repository (the Research Hub's old address redirects here), and domtest.js gains Phase 10, which fails the checks if this page's version labels differ from the engine's, and Phase 11, which checks the front door. Full account: CHANGELOG.md's v4.22 Release Notes.
v4.21Sep 2026CCO relief under inflation, seven counter-intuitive figures explained, and a large-N study (Sep 2026)
runYear() now computes CCO's cost factor as 1 − min(0.50, 0.20 × (buEff / priceIdx) / 1200), with priceIdx = (1 + inflRate)^yr, the index the basket and COLA already use. Through v4.20 it read buEff directly, a nominal amount against a year-0 reference, applied to a nominal basket: an unindexed BU kept its real relief and COLA counted inflation twice. Identical at 0% inflation, so the seed-42 regression, Full Integration, CCO Only and High Automation are bit-identical; Adverse Environment (seed 42: 36.8%, $205,005, 723d, Gini 0.658; v4.20 35.0%, $218,851, 819d, 0.646) and Stress Test (59.8%; v4.20 59.0%) move. harness.js keeps the old rule behind RELIEF_PRICE_LEGACY, and validate checks that it reproduces v4.20. (2) Investigation. node harness.js v421 reproduces seven explanations: the BLEI-poverty hump (peak near 22.6% around year 7, back to the year-0 level at year 24); the saving assumption behind the wealth levels; why recessions add less distress in harsher environments (fewer agents near the line); why the shock-neutral multiplier is the same across environments at the same BU (about 7% of the basket in extra relief); why stabilizers can leave recession years better than calm ones (relief larger than the income loss, no price response); the seed-42 High Automation row of v4.20 (a new realization, then the recalibration); and nominal poverty lines under inflation (at most 1.5 points). The last three are disclosed in Known Limitations, with the CCO+PTH BLEI floor (31.3 days at $1,200, so never BLEI-poor). (3) Large-N. --agents=N sets the population in every harness mode. At 500 seeds × 5,000 agents Full Integration gives 15.26% wealth poverty, 12.48% BLEI poverty and $527,328 median wealth, against 15.27%, 12.43% and $528,624 at 500 agents; across 250–20,000 agents per run its wealth poverty ranges from 15.22% to 15.32%, each ±0.06. (4) Also. A pathway decomposition (pathways: cost relief and octave-driven wages carry most of CCO's effect, conversion proceeds about a sixth), a saving sweep (saving), seed-42 fixtures for all six presets, and regenerated extreme-poverty and stress tables. Two decisions are logged: a consumption rule, and whether poverty lines should be deflated. domtest.js gains Phase 9 (four checks, 82 total), each failing against v4.20. Full account: CHANGELOG.md's v4.21 Release Notes.
v4.20Sep 2026automation risk recalibrated and resampled, a paired validation check, accessibility, and checks that run themselves (Sep 2026)
drawAutomationRisk() drew its Beta(6,1)/Beta(1,6) components through beta(), whose gamma() rejection loop consumes a variable number of random numbers, so the mixture weight re-streamed every later draw of agent construction; at v4.19, changing it from 0.47 to 0.63 moved seed 42 to 15.0% / $583,843 with automation off. Both components now use their exact inverse CDF (U1/a; 1 − (1 − U)1/b), two draws per agent. The weight, CFG.AUTO_HIGH_SHARE, is 0.63: the employment-weighted mean probability of Frey & Osborne's 702 occupations is 0.592, and 0.63 reproduces it (the weight 0.47 gave 0.479, being F&O's share of 2010 employment above 0.7, not a mixture weight). All 702 rows of the plotly/datasets file were checked against the paper's appendix and match on SOC code, rank and probability. Risk enters the wage drag linearly, so the mean sets the aggregate effect. Over 500 seeds: High Automation wealth poverty 25.87% → 29.16%, median wealth $417,131 → $340,928; Adverse Environment 35.86% → 38.42%; Stress Test 58.69% → 61.18%; Full Integration, with automation off, 15.30% → 15.27% (noise). The sampler change alone, at the old weight, moves nothing beyond noise. Risk is still drawn independently of wage, although across occupations log median wage and probability correlate at −0.65; that is the remaining open item. (2) Validation. The non-participant check compared non-participants' poverty in one arm with the whole population's in the other; per seed that swings from −21pp to +3pp in both v4.19 and v4.20, and the new realization put one seed over its 2pp tolerance. It now reads the same agents in both paired arms: never positive over 20 seeds (mean −8.7pp). (3) Accessibility. aria-labelledby on every slider and the seed field, role="group" on every On/Off pair, aria-valuetext on sliders, focusable and named ⓘ tooltips and described preset buttons, tooltips on :focus-visible, and a slider focus ring (removed by outline:none with no replacement). (4) Fixes. A shared link's malformed seed showed "NaN", or "12" while running unseeded; the JSON extreme-poverty block names itself an overlay; the attribution card says its bars are compound effects; the run-configuration line shows both cost anchors; the LHS export no longer claims to suit Sobol or Morris analysis (it suits RBD-FAST or delta first-order indices, checked with SALib on the Ishigami function); the repository README's license statement reflects the v4.8 split. (5) Checks. harness.js validate asserts the v4.20 figures and that a legacy switch reproduces v4.19; unitSuite(), 15 pure-function and property tests, runs against both the harness and the page; domtest.js Phase 8 (ten checks, 78 in all) adds a source-parity check across the 35 functions the two files share verbatim; GitHub Actions runs all three on every push. The first CI run failed two checks: jsdom fires the page's load events after the test harness evaluates the page, so the page's own seed-42 reference run could replace a test's results on a fast machine. domtest.js now suppresses that auto-run; the page itself is unchanged. (7) Wording. The Automatic Stabilizers panel's labels and hints are rewritten in plain language, and COLA is spelled out as cost-of-living adjustment; ids, URL parameters and export labels are unchanged. The engine was not split into a separate file, which would need a build step; the parity check addresses the drift that proposal was about. (6) Rechecked. The stabilizer defaults are still shock-neutral (×1.35; Stress Test ×1.50), and the headline reductions barely move (78.6% / 82.4% against the shipped Baseline). Seed 42 / Full Integration / 20yr: 1,975d · $570,661 · Gini 0.518 · 15.8% · 88.8% (v4.19: 1,965d · $559,223 · 0.534 · 16.6% · 88.5%).
v4.19Sep 2026automatic stabilizers, and CCO cost relief that scales with BU (Sep 2026)
runYear() computes an effective BU each year, buEff = p.bu × stabM × colaF, and uses it at every BU read. stabM is the recession multiplier (fixed, or 1 + k × income loss) when the year's recession loss is at least the trigger; colaF = (1 + inflation)^year above the COLA trigger. Suspended expiry sets decay to 1 while triggered. Emergency enrollment gives the CCO cost relief, for triggered years only, to non-participants whose stored latent CCO uniform falls below partRate + take-up × (1 − partRate). No lever draws a random number. (2) Relief. CCO's cost factor is 1 − min(0.50, 0.20 × buEff / 1200), replacing a flat 0.80; identical at $1,200. The 0.50 cap and the 50% emergency take-up are placeholders. (3) Study. shockRun()/shockStudy() compare each seed with recessions off and on under each rule, and search for the multiplier at which participants' recession-year excess housing distress is zero. Reference settings: ×1.36 (×1.35 in Adverse Environment and CCO Only, ×1.52 in Stress Test). Under the v4.18 flat relief it was ×2.35. (4) Checks. domtest.js Phase 7 (12 checks); 68 in all pass. Full account: CONTRIBUTING.md v4.19.
v4.18Sep 2026extreme poverty as a fifth measure, and the poverty card restyled (Sep 2026)
extremePovertyOf() returns an expected share: economic = EP_Y0_RATE × 0.73 × D/D0, where D is the share of agents in housing distress (cash income plus start-of-year savings below the year's basket at the prices the agent pays) and D0 the same share at year 0; SMI = EP_Y0_RATE × 0.25 × (1 − 0.50 × zone coherence) when PTH and SZH are both on, otherwise unreduced; voluntary = EP_Y0_RATE × 0.02 everywhere. runYear() records start-of-year wealth for it; no RNG, nothing read back. (3) Sources. Year-0 rate: HUD 2025 AHAR Part 1 (745,652 people; about 22 per 10,000). SMI share: Gutwinski et al. (2021), schizophrenia spectrum 12.4% plus major depression 12.6%. Wellness-zone effect: At Home/Chez Soi (housed none of the final six months 16% under Housing First vs 46% under usual care). The 2% voluntary share has no US source; it, the proportionality of economic homelessness to housing distress, and an SMI pathway that does not worsen with the economy are the author's assumptions, to be updated as data sources become available. (4) Results. Seeds 1–500, per 10,000: year 0 22.0, Baseline 73.6 (3%) / 49.3 (0%), CCO Only 22.5, Full Integration 13.2, Adverse Environment 35.5. Varying each constant across a plausible range moves Full Integration's reduction against year 0 only between 36% and 43%. (5) Fixes. domtest.js's version-label check dispatched its event on document while the page listens on window, so it never exercised the fill; two checks pinned the panel at six rows. All three repaired; the tab title now reads META.VERSION. The v4.17 card and preset grid were checked in headless Chromium. The seed-42 regression is unchanged.
v4.17Sep 2026four poverty measures, the papers' headline reconciled against the engine, and an adverse-environment preset (Sep 2026)
runYear() records each agent's wage income, conversion proceeds, own basket cost and gross basket (no RNG, never read back); incomeBasketMetrics() turns them into relative income poverty and basket poverty, and incomeBasketYear0() gives the policy-neutral starting point. (2) Year 0. Wealth and income measures at year 0 are identical across scenarios (same latent population); BLEI is not, because it credits BU and cost relief on enrollment (2.6% under Full Integration rules vs 10.3% for the same agents under Baseline rules), so both are reported. (3) Why the Baseline deteriorates. The median year-0 wage income is $39,945 against a $49,370 basket; 66.6% of agents start in basket poverty and, with no transfers, run deficits from year 1. At 0% inflation Baseline wealth poverty plateaus near 50%; at 3% it reaches 71.8%. (4) Stress, separated. Adverse environment at reference settings: 35.9% wealth poverty; weaker settings in the reference environment: 32.0%; both (Stress Test): 56.8% (seeds 1–500). (5) Relabels. No dynamic reads aggregate CCO participation (sweep: 23.2% at 45%, 20.9% at 55%, 18.4% at 65%), and θ is gated on the zone-coherence slider, not realized PTF density; both are now described as they are and logged as decisions. (6) Fixes. Hardcoded v4.15 version labels; BU Expiry missing from the CSV; an inert LHS PTF column; harness.js's structuralStability() window (Math.max(1) vs the page's Math.max(2)); recessions ported to the harness and matched to the page bit-for-bit at seed 42. The seed-42 regression is unchanged.
v4.16Sep 2026an internal audit: a reproducibility fix, an inflation confound made visible, and two open items closed (Sep 2026)
simulate() advances Your Settings one year per setTimeout step and assumed the shared RNG global was untouched in between. The attribution ablation left its own stream installed between chunks (and restored a stale one when done), validation checks called initRNG() without restoring, and runSim() read the global 50ms after seeding it. Reproduced in jsdom against v4.15: seed 42 gave $545,506 when re-run the instant a run finished and $555,354 during the validation suite (documented: $559,223). Fixed at every boundary — per-year re-assertion, an explicit hand-off from runSim(), per-chunk restore plus a run-generation counter for the ablation, per-check restore for validation. (2) Baseline inflation. pBase uses a fixed 3% CPI; Full Integration, CCO Only and High AI run at 0%. harness.js infl-match 500: Baseline wealth poverty 71.8% at 3% vs 50.5% at 0%; Full Integration 15.3% at 0% vs 28.8% at 3%; the unmatched 56.5-point gap is 35.2 points matched at 0% and 43.0 matched at 3%. The "price-stability hypothesis" that motivates 0% for Compassionism scenarios (Mathematical Framework, above) is therefore an assumed input counted inside the policy effect, not a mechanism output. Shipped as disclosure plus an opt-in toggle (off by default), with the default logged for Duke. (3) Contributing-guide items (e) and (f). The annual schedule is now in the ODD panel; the invariants check asserts, among other things, exactly eight RNG draws per agent-year — the common-random-numbers guarantee every scenario comparison rests on, previously stated only in prose. (4) Documentation. A stale SIU_TO_USD comment in runYear(); recession's scope (it scales CCO conversion proceeds as well as wage income); and PTH appreciation accounting (the code adds the full appreciation to acreEquity and credits the liquid share to wealth — a value-conserving alternative would move seed 42 to $558,001, measured and logged, not applied). Seed-42 regression unchanged. Full technical detail is in index.html's own changelog and CHANGELOG.md's v4.16 Release Notes.
v4.15Sep 2026two independent external audits: another mechanics bug of the v4.14 class, a statistical correction, and verification applied to the release's own handoff (Sep 2026)
How these audits differed from prior ones. MuseAI ran this project's own tooling —
harness.js validate and domtest.js — before reporting, which no external audit had done before; every earlier audit's claims were verified afterwards by a Claude session that first had to build a harness from scratch. Here the findings arrived already grounded in the project's regression machinery, so verification meant reproducing and extending them (scope sweeps, negative controls) rather than establishing them from nothing. Grok's review deliberately avoided re-flagging items already logged, and reached its own conclusion that it found no additional mechanics bugs on the default or documented paths — independent corroboration that the fixes below are the whole story for the documented configurations.
Bug: PTH's inflation damping was a flat toggle-triggered discount. The line directly beneath the v4.14 PTF fix,
if(p.pth&&inflRate>0)inflRate*=0.90, applied a flat 10% reduction to the whole population's cost inflation whenever PTH was toggled on and inflation was nonzero — regardless of pthUptake. It is the v4.14 defect class again in a coarser form: the PTF line at least read a slider (the wrong one); this one referenced no adoption-scale quantity at all. Confirmed by isolating the computation: at 2% inflation the factor was bit-identically 0.90× at 5%, 20% and 50% of a 500-agent population in PTH — and PTH switched on with zero members damped inflation too (reproduced live in domtest.js against an unmodified v4.14 page: $54,147 vs $45,444 median wealth for otherwise-identical runs). Fixed as inflRate*=(1-pthMemberFrac*0.10), where pthMemberFrac is the population's realized PTH share, counted the same no-RNG way as PTF's adoption fraction. The 0.10 coefficient (=1−0.90) is unchanged and is now the ceiling at 100% membership, exactly as the PTF fix kept its own 0.5. Unlike PTF, PTH membership is drawn once at construction and never changes during a run, so there is no organic adoption to track across years — the fraction is simply the realized initial share, which carries Bernoulli sampling variance around the slider (a 5% slider realized 6.4% at seed 42).
Scope, proven by sweep rather than argued. Full Integration and hiAI have zero inflation; Baseline and CCO-Only have PTH off. Rather than stop at reading that off the configurations, all five shipped presets were run under the old and new engines at seed 42 and every agent's final wealth compared: four of five are bit-for-bit identical (hiAI included, with AI automation on); only Stress Test differs — the only preset combining PTH with nonzero inflation. A PTH-uptake sweep on a synthetic isolate (CCO + PTH + 4% inflation) shows the fix is identical at every uptake when inflation is 0, identical at 100% uptake for any inflation (the new formula equals the old flat one at full membership), and diverges most at low uptake (median wealth $84,783→$45,595 at a 5% slider; $157,016→$118,888 at 20%; $366,914→$358,858 at 75%). The direction is the opposite of the v4.14 PTF fix and worth stating plainly: the PTF fix strengthened damping because real adoption ran above its slider; this one weakens it because at a 10–20% realized uptake the flat factor overstated what a small PTH-participating minority should do to population-wide inflation. Harness-computed Stress Test (seed 42/20yr, shocks and automation off — the harness's simplified recession handling doesn't reproduce the in-app preset's stochastic-shock path): poverty 41.8%→43.2%, Gini 0.696→0.702, median wealth $158,155→$143,494, median BLEI 482d→436d. The only internal validation check that reaches this line,
ptf_infl, shares the same PTH uptake in both arms and was re-verified 5/5. A sweep of every population-level setting read in runYear() for the same pattern — a scalar or bare toggle standing in for realized membership — found no further instance (the remaining reads are SZH's zone-coherence scalar, CIP's participation scalar, and the PTF-cap comparison, each by design).
Four smaller items. Robustness:
Math.max(1,undefined) is NaN, so a caller that ever omitted the BU-expiry parameter would have got a NaN decay; measured, that silently zeroes every CCO participant's wealth each year (via the existing isNaN rescue) with no error thrown. Never a live defect — every current caller supplies the value — but now guarded (p.expiry||1, a no-op for the UI's 1–6, verified bit-identical). Statistics: the Run 10×/50× "95% CI" used a fixed z=1.96 at every n. At n=10 the correct multiplier is Student's t(9)=2.262: the old interval was about 13% too narrow (the correct one is about 15% wider) and a nominal 95% interval covered only about 91.8% for a normally-distributed statistic (n=50: t=2.010, about 94.4%). All 36 entries of the t table were checked against an independent implementation, and the summary header now states the t and df actually used. Display-only — no RNG, no agent state. Documentation: the interactive tool's Empirical Calibration panel had a fourth stale PTH card that v4.12's staleness sweep missed — housing-cost reduction described as "35% of SIM_COST_SCALE" (a constant no function reads) and a flat 50% liquidity haircut (superseded in v4.5 by the tenure ramp). Corrected. Layout (this page): the header's collapsible “Version highlights” block had a 700px max-height cap that its own content already exceeded (roughly 900px on desktop by estimate, considerably more on a phone), so its oldest lines were silently clipped; the cap is raised to 8000px, the same technique the Version History panel uses. That figure is estimated from character counts, not measured in a browser — worth a look at a couple of widths after deploying.
What checking the handoff itself turned up. This release's own working documents were verified with the same discipline as the audits, and the verification found errors in them: a figure described as "~13% larger" that was really 15.4% (it is the fixed-z interval that was 13.4% too narrow); a partially-built draft of the interactive tool that had silently dropped many CSS comment blocks and was therefore rebuilt from the untouched v4.14 source with assertion-checked edits rather than completed; a description of the missing-expiry failure that understated it; and a draft note that mischaracterized one audit's suggestion. Recorded here because catching them is the point of the practice.
What did NOT get done, and why: PTH entry/exit dynamics (membership remains a one-time draw, a design simplification since v4.0 that belongs with the rollout/transition-path item in CONTRIBUTING.md); everything logged at v4.14 (flow-of-funds ledger, monthly BU tranches, pathway decomposition, λ heterogeneity, employment-transition and macro-recession models) remains open and unchanged; and one item is a repository action for Duke rather than a code change — Grok's suggestion of formal Git tags or GitHub Releases per version, logged in CONTRIBUTING.md. Full technical detail, including the uptake-sweep table, is in
index.html's own changelog and CHANGELOG.md's v4.15 Release Notes.
v4.14Sep 2026a two-pass external ChatGPT audit finds two genuine runYear() mechanics bugs (Sep 2026)
index.html and CONTRIBUTING.md, then a second pass explicitly focused on "the economic mathematics and causal structure of runYear()," undertaken because the reviewer judged the first pass had exhausted what code-level and documentation-level review could find and that the harder remaining questions were economic rather than editorial. That second pass earned the distinction: it found two genuine defects that seven prior audits — several of them detailed, adversarial, and methodologically careful — and this project's own six-check internal validation suite had all missed.
Bug 1: the BU-expiry slider implemented two behaviors, not six. The UI presents "BU expiry (months)" as a 1–6 slider. The code consuming it,
var decay=p.expiry<2?0:0.7, is only sensitive to whether expiry clears a threshold of 2 — every value from 2 through 6 evaluates identically. Verified directly before any fix was written: expiry=3 and expiry=6 produced bit-identical simulation output on the unedited engine, to the dollar and the decimal point. Replaced with a continuous annual-approximation decay=1-1/expiry — a documented interpretation in the same spirit as pthLiquidShare()'s existing linear interpolation for a comparably under-specified continuous quantity, not the full monthly-BU-tranche rebuild a "proper" fix would need (logged as its own future-work item, so the shipped fix's honest scope is on record). Verified inert on everything documented: all five shipped presets, every internal mini-simulation, and every OAT/LHS sweep use expiry=1 and never vary it — confirmed by grep before writing a line of code — and at expiry=1 the new formula gives decay=0, bit-identical to the old behavior. Only a user who manually drags this one slider is affected; six positions now produce six genuinely distinct, monotonically-ordered results instead of two.
Bug 2: PTF's inflation-damping effect tracked the initial slider, not actual adoption.
if(p.ptf&&inflRate>0)inflRate*=(1-p.ptfShare*0.5) used p.ptfShare — the static, initial-adoption-probability slider — for a population-level macro effect, while the exact same function already computes the actual current adoption fraction (ptfAdoptFrac) a few lines later, for the Bass-diffusion adoption term. That correct quantity simply was never reused for the inflation effect, even though the per-agent cost factor a few lines further down already correctly gates on each agent's real, current PTF membership flag rather than the slider — a state-variable/effect mismatch, not a design choice. Fixed by moving the computation earlier and reusing it (no RNG draw, so no stream position shifts anywhere). Verified inert on every documented figure — Full Integration, CCO-Only, Baseline, and hiAI each have inflRate=0 or ptf=false at this exact line, so none of them are touched, confirmed by the unchanged seed-42 regression below. Of the five shipped presets, only Stress Test combines both conditions; of the six internal validation checks, only ptf_infl exercises this line — re-run after the fix and confirmed still passing 5/5. Effect size where it applies (Stress Test, seed 42, 20yr): poverty 43.0%→41.8%, Gini 0.703→0.696, median wealth $141,814→$158,155 — actual PTF adoption running above the static slider means the fix applies stronger damping than the bug did, the economically expected direction.
Both fixes ported into
harness.js for parity, and both cannot move a single documented figure on this page — the seed-42/Full Integration/20yr regression (Median BLEI 1,965d, Median wealth $559,223, Gini 0.534, Wealth poverty 16.6%) is bit-identical, confirmed by re-running harness.js, by driving the fixed engine through domtest.js end-to-end in a real DOM, and by isolated before/after checks on every one of the handful of configurations either fix actually touches. domtest.js gained six new checks this release, two of them direct regression guards: if either bug is ever reintroduced, the harness fails immediately rather than waiting for another audit to rediscover it.
One new reporting feature: the Wealth Floor Diagnostic. The audit's general-review pass asked for an in-app diagnostic showing how much of a headline Baseline-vs-Your-Settings gap reflects
WEALTH_FLOOR's exact value rather than a genuine policy effect — a fair question this project had previously answered only offline, in CONTRIBUTING.md's own four-floor-value sensitivity sweep (v4.8). Two new KPIs show the share of the final-year population pinned exactly at the floor, for Your Settings and Baseline — a same-run companion to, not a replacement for, that offline sweep (seeing today's pinning doesn't show how sensitive it is to the floor's own value). Pure snapshot of already-final agent state; exported to CSV and JSON.
Documentation clarifications, no mechanics changes: EDC-adjusted Gini is now explicitly labeled — in its KPI tooltip, in the engine's own code comment, and in Mathematical Framework §5 below — as a constructed inequality index (a wealth stock minus one year of EDC-implied extraction, a flow), not conventional net-worth accounting; the formula itself is unchanged and still matches BLEI paper Table 6 exactly. A dual-cost-anchor table (
LIVING_WAGE_ANNUAL vs. BASE_DAILY_COST) was added to the Empirical Calibration card. The ODD panel's Design Concepts card, and an inline comment at the octave-advancement site inside runYear() itself, now name the reinforcing feedback loops the audit's causal-structure review correctly identified explicitly: BLEI(prior year)→wage growth→wage→FBS→octave→{next year's wage growth, next year's conversion-rate ceiling}, and separately PTH→lower cost→higher FBS→octave→wage. Neither loop is a bug — both are the intended design — but a headline "CCO effect" or "PTH effect" is a compound of several coupled mechanisms, not one isolated channel, and nothing previously said so explicitly. A new Known Limitations entry states plainly that "recession" only ever shocks earned wage income and "AI automation" is a reduced-form wage-growth penalty, both narrower than their names might suggest. The standing "not a forecast" banner gained one sentence, close to the audit's own suggested wording, making explicit that results demonstrate the implications of the implemented assumptions rather than establishing the empirical validity of those assumptions.
What the audit got right that this project had already substantially addressed, checked rather than assumed: the audit's call to "formally divide validation into Levels 1–5" is, verbatim, already the validation panel's own subtitle since v4.2. Its FBS λ concerns and its recommended FBS₅₀ = ln(2)/λ reparameterization were already shipped at v4.4/v4.6. Its stock-flow accounting concern has been an explicit Known Limitation since v4.0 — the audit's more detailed proposed ledger schema is logged as an elaboration of that same item, not treated as new. Its suggestion to split
index.html into modules is the identical suggestion an external ChatGPT audit made at v4.7, already rejected then as conflicting with this project's explicit single-file distribution constraint — same conclusion here. Its call for real-browser (Playwright) testing beyond domtest.js was already an explicitly flagged open decision in v4.13's own Model Architecture Feedback. Its suggestion to rename the "System Stability" KPI (a real, already-documented concern since v4.4) was considered and declined, for consistency with the audit's own stated preference elsewhere in the same document to define "wealth" precisely rather than rename it — renaming a KPI referenced throughout years of exported CSV/JSON columns and documentation is disruptive for a label already comprehensively caveated at the point of display; logged as an option for Duke's own judgment, not decided unilaterally.
What did NOT get done, and why: several of the audit's recommendations are legitimate but substantially larger than an audit-response release should take on unilaterally — a full flow-of-funds ledger with explicit conservation identities, monthly BU tranches (the fuller version of the expiry fix actually shipped), CCO/PTH pathway decomposition via structured ablation, λ heterogeneity beyond a fixed-and-independent draw, AI automation as a genuine employment-transition model, a fuller macro-recession model touching assets/employment/transfers, an expanded invariant/mechanism/calibration test taxonomy, and an explicit enumeration of
runYear()'s full annual state-transition schedule (deliberately deferred rather than risk transcribing an inaccurate sequence alongside everything else in this release). Each is precisely scoped in CONTRIBUTING.md's Model Architecture Feedback and Good First Issues for whoever picks it up next. Full technical detail, including the isolated before/after verification tables for both mechanics fixes, is in index.html's own changelog and CHANGELOG.md's v4.14 Release Notes.
v4.13Sep 2026an independent audit pass, and the first release to find defects by running the tool rather than reading it (Sep 2026)
node --check, JSON-LD parse, harness regression) is structurally incapable of catching, because none of those checks ever runs the page.
Defect 1: a shared "Copy parameters as URL" link silently disabled every preset tooltip.
applyParamsFromURL() cleared the preset buttons' "active" highlight by resetting each button's class to a bare pbtn — which also removed .tip-host, the class the v4.5 preset-description tooltips hang off. Every visitor arriving through a shared link therefore lost all five preset hover descriptions for the entire session, silently. This is the same bug v4.5's own notes record catching in applyPreset() ("would have silently stripped the new tooltip class") — the identical pattern was written into applyParamsFromURL() in that same release and survived seven subsequent audits, for a specific and instructive reason: the shared-link entry point is the one path a reviewer reading index.html top to bottom never mentally executes, and the one path no automated check had ever taken.
Defect 2: the parameter-sensitivity panel duplicated its tip box on every run.
renderSens() wrote its "💡 One-at-a-Time (OAT) Sensitivity" note using insertAdjacentHTML('afterend', …), which inserts a sibling of the container the function then clears with innerHTML — so the clear never touched it, and renderSens() runs once per completed simulation. Three runs in one page session left three stacked copies. Confirmed in the DOM harness (3 boxes after 3 renders before the fix, 1 after), now a single stable-id node updated in place.
A metric that reported a perfect score where it had no data.
structuralStability() measures the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter, with the window floored at one data point. The Simulation-years slider's minimum is 5, and for any run of 5, 6 or 7 years that quarter rounds to a single value — a variance of one observation is undefined, the coefficient-of-variation helper correctly returns 0, and the metric returned exactly 0.99, its own clamp ceiling, for every short run this tool has ever executed, whatever actually happened in it. Verified rather than reasoned: a strictly-increasing five-point series scored 0.990000, as did every other short series tried. The window floor is now 2. A sweep across every supported run length confirms 8–30 years are bit-identical to v4.12 — so every preset, the 20-year reference configuration, and the seed-42 regression baseline are untouched; only 5–7 year runs change, and only in this one displayed number. A matching Known Limitations entry now says plainly that System Stability needs ~8+ simulated years to mean anything.
Stale documentation inside the engine, where v4.12 had fixed it only around the engine. v4.12 corrected the interactive tool's ODD Protocol panel, its Empirical Calibration panel, and its top-of-file unit-system comment for describing pre-v4.3/pre-v4.4 mechanics as current. The same staleness was sitting inside
runYear() the whole time — the function all three of those panels describe, and the copy a contributor editing the engine actually reads. Four specific claims, each contradicted by the code beside it: that SIM_COST_SCALE "remains load-bearing" through the SIU_TO_USD derivation "that BLEI/FBS/Gini still use" (SIU_TO_USD was retired in v4.4; grep confirms SIM_COST_SCALE now has zero read sites anywhere in the file); that dollarCost is consumed "only by the FBS advancement gate" (it has two consumers — the FBS gate and the PTH equity block); that Yusd uses SIU_TO_USD (the line directly beneath reads WAGE_TO_USD); and that "FBS doesn't feed back into wealth" (it does, through octave advancement into conversion capacity and wage growth — precisely the feedback path v4.4's own notes give as the reason FBS_LAMBDA needed a compensating rescale). All corrected in place with short notes recording what was wrong, per this project's established practice of documenting a correction rather than silently swapping text.
Two reporting additions, both pure transforms of figures the engine already produces. Cumulative Poverty Exposure reports the mean number of years an agent spends below the wealth-poverty line, and below the BLEI poverty line, across the whole run — for Your Settings against the Traditional Welfare Baseline. This answers the "final-year snapshot only" gap the v4.12 external audit raised directly (its person-years-in-poverty suggestion), and the derivation is exact rather than approximate: each year's figure is a share of the same population, so summing the shares gives the average years-poor per agent. The result reframes a familiar number — at the seed-42 reference configuration the final-year wealth-poverty rate is 16.6%, but the average agent spends 4.8 of 20 years below that line (Baseline: 11.8), and 3.7 years below the BLEI poverty line (Baseline: 11.1). Its one honest limit is now a Known Limitation: this is aggregate exposure, not a spell-length distribution, and cannot distinguish "everyone poor for three years" from "30% of agents poor for ten." Second addition: the v4.12 dominance check was screen-only — visible but not archivable or citable with the run that produced it. It is now captured into the run snapshot and written to both export formats along with the view it was computed in and the number of threshold grid points evaluated, and the on-page caption states plainly that dominance is checked at the plotted thresholds only and describes one stochastic run rather than a confidence claim.
Smaller fixes. The Wealth Poverty KPI prefixed a hardcoded "▼" to a signed delta, so a configuration doing worse than baseline rendered as "▼ -3.2pp"; the arrow now follows the sign. Two grids built inside JS template strings still used a bare
1fr track — the exact CSS Grid overflow pattern v4.8 swept out of the stylesheet, missed because that sweep grepped CSS and these are inline strings; the Monte Carlo summary also never reflowed on a phone and now does. Toggle buttons expose aria-pressed, closing for the sidebar controls the same gap v3.7 closed for the charts. Exported JSON asserted a flat "CC BY 4.0" license, unchanged since before the v4.8 relicensing split, and the CSV's mechanics list was still headed "--- v4.0 FIXES ---" nine releases later; both corrected. CFG.POVERTY_LINE now carries a note at the constant itself recording that it has no citation anywhere in the codebase — an open item in CONTRIBUTING.md since v4.10, with nothing at the code to warn a reader who never opens that file. On this page: the header subtitle still read v4.9 and the Version History toggle label still read "v3.1 → v4.9," both four releases stale — and that second label is the very one v4.9's own entry below records fixing when it had drifted to "v3.1 → v4.7." It has now gone stale, and been caught, twice. In CONTRIBUTING.md, two release sections — v4.11's and v4.5's — had lost their ## …Release Notes headings entirely, so each rendered as a continuation of the section above it; the second was found only because the first prompted listing every heading in the file rather than assuming a single casualty. Both restored.
What did NOT get done, and why: the DOM harness verifies behavior, not appearance — it cannot confirm CSS layout, tooltip positioning, or Chart.js pixel output, so those still want a human look after deployment, and the five structural proposals logged in v4.12 (executable test hierarchy, positional-argument refactor, calibrated current-policy scenario, rollout/transition model, per-agent longitudinal metrics) all remain open and unstarted. The per-agent longitudinal layer is now the most clearly motivated of the five, since the new exposure metric runs directly into its absence. Full technical detail in
index.html's own changelog and CHANGELOG.md's v4.13 Release Notes.
v4.12Sep 2026documentation-and-verification pass prompted by a Perplexity AI audit, plus a dominance-check addition (Sep 2026)
One real documentation bug, found and fixed by direct computation. The interactive tool's own in-app "Cumulative Bug Fixes" note on the v4.6 PTF adoption cap stated that seed 42 / Full Integration / 18% slider converges to 23.8% under the shipped agent-granularity cap — inconsistent with this document's own v4.6 Release Notes, which have always said 19.0%, and the audit flagged the inconsistency without being able to say which side was right. A Node.js harness built this session (reproducing the documented seed-42 regression figures exactly before being trusted for anything else, the same discipline every prior harness in this project has followed) settled it: 19.0% is correct. Tracing
ptfLiveCount/ptfCapAllows() and reproducing it computationally shows why: seed 42's year-0 Bernoulli draw already lands at 19.0% (95/500 agents), above the 18% cap, so the cap blocks every subsequent admission for the entire 20-year run and the final share holds at the year-0 level rather than growing. 23.8% was the output of an earlier, discarded once-per-year-checked prototype — never the shipped implementation — and appears to have been carried into the interactive tool's own documentation at v4.6 by mistake, uncaught until this session. A 10-seed sweep (seeds 1–10) confirms the general mechanism works exactly as designed: seeds whose year-0 draw starts below 18% grow and land at exactly 18.0% (500×0.18=90 is a whole number for this specific slider/population combination, so there's no fractional-agent overshoot to speak of here); seeds already at or above 18% at year 0 simply hold there, as seed 42 does. index.html's own panel is corrected; this document's figure was already right and needed no change.
Stale technical documentation, found in three places, all describing superseded mechanics as if current. The interactive tool's ODD Protocol panel had a "State Variables" card still stating
automationRisk ∈ [0.2,1.0] — the retired pre-v4.3 uniform draw's exact bounds — directly contradicting a correctly-updated card two rows below it in the very same panel, which has said "47%/53% bimodal mixture" since v4.3. The same panel's "Submodels" card still described the annual wealth update as SIM_COST_SCALE-driven and BLEI's wage term as SIU_TO_USD-converted, both accurate only through v4.2 — superseded by v4.3's wealth-loop unit fix and v4.4's SIU_TO_USD retirement, respectively, neither ever updated in this specific card even though the dedicated Cumulative Bug Fixes entries for both releases were kept current. The Empirical Calibration panel's "Daily vs. simulation cost scales" card was framed entirely around "what v4.0 fixed and what it deliberately didn't" and still asserted the main wealth loop was "deliberately NOT changed" — v4.3 changed exactly that, over a year of shipped releases ago by this project's own internal chronology. All three are rewritten in index.html to describe the current, v4.4-onward state while preserving the original v4.0 reasoning (still the correct explanation for why v4.0 shipped a partial fix rather than none) — the top-of-file UNIT SYSTEM code comment had the identical problem and got the same treatment. None of this affected any computed output; these are description-only fixes to panels whose job is describing the model, not running it.
New feature: a dominance check on the Threshold Sensitivity charts, directly implementing the audit's strongest concrete suggestion. The audit recommended the charts "test for first-order stochastic dominance over the displayed range... a clear statement when curves cross... do not imply a universal welfare ordering when curves cross." A new
checkDominance() function, exercised via both chart callouts, states plainly whether Your Settings sits at-or-below Baseline (and separately CCO-Only) at every threshold shown, at-or-above everywhere, or crosses — and if it crosses, roughly where. This is an honest check in both directions, not a feature built to only ever confirm the expected answer: at the seed-42 Full Integration reference configuration it reports dominance (Your Settings beats both comparisons everywhere, on both charts, in both the headcount and gap views) — a genuine, computed finding rather than an assumption wired into the feature, confirmed via the same regression-validated harness used for the PTF-cap fix above. A Stress Test preset re-run (lower participation, higher tax, lower BU) also showed dominance rather than a crossing in this project's own mechanics, so readers should read a "dominates" result as a real finding about this model's structure, not evidence the check only ever says yes — the crossing-detection path was separately confirmed to fire correctly against constructed adversarial curve pairs during unit testing (9 cases: both dominance directions, identical curves, a single crossing with its location correctly identified, weak dominance with tied values, and floating-point-noise edge cases correctly not flagged as spurious crossings), and against real simulated output via a full end-to-end Node.js integration test that evaluated the actual edited index.html script — not a hand-copied approximation — reproducing the documented seed-42 regression figures exactly (median wealth $559,223, median BLEI 1,965d, Gini 0.534, wealth poverty 16.6%) before the dominance check was ever run against them, then driving the real renderThresholdSensitivity()/drawThresholdCharts() render path in both headcount and gap views to confirm the callout text builds correctly and without error. Purely a reporting transform — computes no new agent state, draws no RNG, cannot affect the Main run, Baseline, or CCO-Only trajectories.
Smaller fixes. The poverty-gap toggle's own hover tooltip called the v4.11 metric "the poverty gap ratio" — but what it shows is the absolute dollar/day shortfall, not the textbook normalized ratio, which is the entire reason v4.11 reports it that way (see that release's own entry, above); corrected to avoid the word "ratio." The sidebar's "Run Validation Suite" button kept the pre-v4.2 name three releases after the panel and heading it opens were renamed "Internal Consistency & Behavioral Test Suite" specifically to avoid overstating what six internal checks establish; button text now matches. The Traditional Welfare Baseline preset's tooltip called it "the real-world reference point," language that could read as a claim the scenario models actual U.S. transfer programs (SNAP, EITC, TANF, Medicaid, unemployment insurance) rather than the absence of any offsetting mechanism at all; softened, with a matching new Known Limitations entry in
index.html.
What the audit got right that this project had already extensively documented, and was not treated as a new finding. The Comfortable/Flourishing tier-label split at the identical 730-day threshold — flagged by the audit as risking an inflated cross-scenario comparison — has its own dedicated "v3.4 naming note" on this very page (further up, under BLEI Temporal Stability Tier System) and appears explicitly in the System comparison table's own caption; it is a deliberate, extensively pre-documented framework narrative choice (the top tier is called "Flourishing" specifically when CCO+PTF's generative mechanics make it structurally attainable, "Comfortable" when it's merely aspirational), not a previously-hidden defect, and was not changed unilaterally — this is Duke's framework-design call, not a code-audit finding. The audit's BU-conversion "reciprocal debit" concern doesn't hold against the actual
runYear() code either: spend is subtracted from a.buBalance in the same statement block that computes convGain from that identical spend amount — the two state variables (unspent balance, wealth) are not double-counting the same entitlement, contrary to the audit's speculation. Several other findings — the internal-validation-suite framing, the LHS export's fixed-seed design, the CCO conversion "ledger" suggestion — restate design choices or limitations already documented at length (the suite's Level 1-3 ODD framing since v4.2, the LHS seed convention explicitly matching OAT's own since v4.6, the "tracked but not production-constrained" conversion-ledger caveat in Known Limitations since v4.0) and were not treated as new.
What did NOT get fixed, and why: the audit's larger structural proposals — a full CI-style test hierarchy, replacing positional function arguments with context objects across the engine, a full per-agent stock-flow ledger, a fourth "calibrated current-policy" comparison scenario, a rollout/transition-path model, person-years-in-poverty and time-to-tier-attainment metrics — are each reasonable, and each is a substantial engineering or framework-design undertaking in its own right, not a documentation fix; logged in CONTRIBUTING.md's Good First Issues and Model Architecture Feedback for Duke's consideration, not attempted here. Full technical detail in
index.html's own changelog and CHANGELOG.md's v4.12 Release Notes.
v4.11Sep 2026poverty-gap ("avg. shortfall") view, prompted by an external methodology review of v4.10 (Sep 2026)
What shipped. A new "Avg. shortfall" toggle switches both existing charts (wealth and BLEI) to a poverty-GAP view — FGT-1 in the same literature (Foster, J., Greer, J., & Thorbecke, E. (1984). "A Class of Decomposable Poverty Measures." Econometrica, 52(3), 761-766) — the mean per-capita shortfall below each threshold, averaged across the whole population (agents at or above the line count as zero). One deliberate departure from the textbook definition: FGT-1 is normally a threshold-normalized ratio, but normalizing by the threshold breaks down at threshold≤0 — and the wealth chart's own range deliberately extends to −$20,000 specifically so Baseline's
WEALTH_FLOOR-pinned distribution renders as a real step (a v4.10 design choice, unaffected here). Reporting the absolute per-capita shortfall, in the same dollars/days as the chart's own axis, sidesteps that division problem while reading naturally as a direct "cost to close the gap" figure.
Validated the same way v4.10's own function was, plus one further cross-check. The core function (
povertyGapAvg(), built on a one-time prefix-sum pass per distribution to keep the same O(log n)-per-query budget as povertyCDF()) was checked against a naive brute-force reference across 22,210 random trials — including arrays specifically constructed to mimic WEALTH_FLOOR pinning, since that's Baseline's actual shape — before this touched the shipped file; zero mismatches beyond floating-point rounding. A full end-to-end Node.js integration test then drove the actual edited script against the documented seed-42/Full Integration/20yr configuration for both Full Integration and Traditional Welfare Baseline, confirming the existing headcount figures were undisturbed (exact match, 16.6%/13.6%) and cross-checking the new function against an independent numerical integral of the already-validated headcount curve — a genuine mathematical identity (mean absolute gap at a threshold equals the integral of the CDF up to that point), not a restatement of the production code. One apparent disagreement, immediately adjacent to WEALTH_FLOOR where 68.6% of a sampled Baseline population sits at exactly one value, was tracked to the cross-check's own coarse numerical resolution near that sharp discontinuity — confirmed by an independent direct per-agent sum matching the production function's output exactly, and by showing the cross-check's own error shrink from 0.171 to 0.017 to 0.0016 as its resolution was refined 100-fold. The production function was correct throughout; only the auxiliary check needed a finer grid to agree with it.
The same review's data-gathering follow-up, checked with the same rigor. Two long-open items from this project's Good First Issues were separately handed to the same external system. Its 51-state, Census Vintage-2025-weighted recomputation of
LIVING_WAGE_ANNUAL ($24.62/hr population-weighted, versus $23.7355/hr unweighted — both computed from current MIT data, the unweighted figure landing almost exactly on the currently-shipped value) had five of its 51 state-level inputs independently spot-checked against live MIT fetches this session and matched exactly, plus a partial reconstruction (the four largest-population states) confirming the population-weighted figure's direction and rough magnitude — but the full 51-state weighted arithmetic itself was not independently re-derived to the dollar, so the figure is documented as well-sourced reference data, not adopted into the engine, consistent with this constant's standing rule that any change needs its own dedicated mechanics-audit release. Its claim that the occupation-risk Kaggle dataset's license is actually unspecified — directly contradicting a v4.8 session's "third-party card says MIT" finding — could be neither confirmed nor refuted (Kaggle remains unreachable to automated fetching), which on its own means the earlier "MIT" finding should now be read as disputed rather than corroborated. The search for an answer surfaced something more useful than the answer itself: a 702-row occupation-automation dataset (exactly matching Frey & Osborne's own count) in the plotly/datasets GitHub repository, under a genuine first-party MIT license — a meaningfully cleaner starting point than the Kaggle listing, with one honest caveat of its own (an open issue in that repository questioning whether its MIT grant cleanly covers third-party-sourced data files).
What did NOT get fixed, and why: adopting the population-weighted
LIVING_WAGE_ANNUAL figure into the engine (deliberately — see above); a row-by-row fidelity check of the new plotly/datasets lead against the Frey & Osborne appendix; a normalized (ratio) variant of the poverty-gap view alongside the absolute one shipped here; every other long-standing open item this session didn't touch — unchanged from v4.10. Full technical detail in index.html's own changelog and CHANGELOG.md's v4.11 Release Notes.
v4.10Sep 2026Poverty & Wealth Threshold Sensitivity, prompted directly by the framework's author (Sep 2026)
LIVING_WAGE_ANNUAL to $49,370, and v4.9's Meta AI audit tried to overturn it with a different single figure that turned out to rest on stale data. Rather than resolve that pattern by defending one more number, two new charts (interactive tool) show the full range instead of a point estimate: the population's cumulative distribution of final-year wealth and BLEI across every plausible threshold at once — the standard "poverty dominance" approach from the poverty-measurement literature (Atkinson, A.B. (1987). On the measurement of poverty. Econometrica, 55(4), 749-764; also the World Bank's practice of reporting global poverty at several lines simultaneously, e.g. $2.15/$3.65/$6.85 per day). Baseline, CCO-Only, and Your Settings are overlaid on the same axes: if one scenario's curve sits below another's across the whole range shown, it reduces poverty regardless of exactly where a reader believes the "true" line sits.
What is, and isn't, explorable this way, and why. Confirmed by grep before writing any code:
CFG.POVERTY_LINE has exactly one call site in the entire engine (inside calcMetrics()'s poverty computation) — a pure post-hoc classification threshold on already-computed wealth, never touching RNG or dynamics, so exploring it across a range costs nothing extra. CFG.LIVING_WAGE_ANNUAL, by contrast, is genuinely baked into runYear()'s annual wealth update — a flow/cost anchor that mechanically shapes the wealth trajectory itself. Making that explorable the same way would mean re-running the simulation once per candidate value, not re-reading an already-computed array — a materially larger, mechanics-adjacent feature, deliberately out of scope here and flagged as the clear next step (CONTRIBUTING.md's Good First Issues). One further nuance worth naming: the BLEI tier cutoffs look similar to POVERTY_LINE on the surface but aren't in the same free category — BLEI_PRECARIOUS_MAX (30 days) is read twice inside runYear() itself, gating both a wage-growth bonus and a PTF-adoption bonus, so it's genuinely mechanics-relevant. The new BLEI chart doesn't touch this: it reads the already-computed continuous BLEI score at many threshold points for display, which is exactly as safe as POVERTY_LINE exploration — it's the CFG constants themselves that would be unsafe to slide, not the act of evaluating an already-computed value at a different point.
One external reference point, verified the way this project now verifies everything external. A new
CFG.FED_POVERTY_LINE_1P constant ($15,960/yr, the 2026 HHS ASPE guideline for a one-person household) appears as labeled callout text beneath the wealth chart — explicitly marked "external reference, not a simulation parameter," same category as CFG.NORDIC_GINI elsewhere in this file. A first fetch attempt against the live ASPE page returned a stale cached snapshot from 2017 (the 1-person figure it showed, $15,060, was immediately recognizable as years out of date) — caught rather than used, and cross-checked instead against an independently-dated primary-source PDF (a Lifeline Safe Connections Act eligibility table, explicitly dated January 15, 2026, HHS-sourced) plus three further independent citations, all converging on $15,960. A much easier verification than the LIVING_WAGE_ANNUAL episodes — a single government-published number, no methodology dispute attached — but the same "don't trust the first thing that loads" discipline applied anyway.
Validation. The core
povertyCDF() function (binary search over an already-sorted array) was unit-tested against a naive linear-scan reference across 12,500 random trials before touching the actual file — zero mismatches, including the realistic duplicate-heavy case of many agents pinned exactly at WEALTH_FLOOR. After integration, a full end-to-end Node.js test evaluated the actual edited script in a stubbed DOM-free sandbox, drove the real makeLatentPopulation()/instantiateAgent()/runYear() functions against the documented seed-42/Full Integration/20yr configuration, and cross-checked the new function's output against calcMetrics()'s and bleiMetrics()'s own existing figures at the model's default thresholds — exact match on both (16.6% wealth poverty, 13.6% BLEI poverty). Chart.js rendering/DOM behavior itself is not verified in an actual browser, the same sandbox limitation as every CSS/UI change since v4.8.
What did NOT get fixed, and why: making
LIVING_WAGE_ANNUAL (or BASE_DAILY_COST) explorable the same way — deliberately, mechanics-adjacent work belongs in its own audited release; additional named external benchmarks beyond the one federal guideline added here; an interactive slider/marker layered on the static curves (Chart.js's own hover tooltips already approximate this for free); every long-standing open item this session didn't touch (WEALTH_INIT_MU/SCF gap, WEALTH_FLOOR/TARGET_* recalibration, occupation-stratified automationRisk, unbounded long-horizon wealth growth) — unchanged from v4.9. One small, unrelated finding caught in passing while adding this entry: POVERTY_LINE itself has never carried a citation anywhere in this codebase, unlike almost every other calibration constant — now flagged as its own open item in CONTRIBUTING.md's Calibration Validation table rather than left unnoticed. Full technical detail in index.html's own changelog and CHANGELOG.md's v4.10 Release Notes.
v4.9Sep 2026a table-overflow fix and a verified external audit (Sep 2026)
Table-overflow fix, traced to a specific missing rule. The System comparison table already scrolls horizontally when it needs more room than it has — it sits inside a container with its own
overflow-x:auto. The Social Security-Anchored Cohorts table (added v4.5) is a plain table dropped into a card with no such rule, so when its six nowrap column headers needed more width than a typical 100%-zoom viewport had left after the sidebar, the overflow ran into a page-level overflow-x:hidden rule (added defensively so no single wide element could blow out the page) and got clipped rather than scrolled. At 80% zoom there's simply more CSS-pixel width available, so the same table never hit that ceiling. Fixed with the same technique the System comparison table already uses, scoped to just this element via a small reusable class. Not verified in an actual browser (same sandbox limitation as v4.8's own CSS fixes) — a visual check at a few zoom levels would be worth doing after this deploys. Separately, Duke confirmed the v4.8 tooltip fix is working correctly in production.
The Meta AI audit: one contribution verified correct, one verified wrong. Meta supplied two things beyond the audit text itself: a modular (ES-module) rewrite of the v4.8 Node.js harness, and a proposed recalibration of
LIVING_WAGE_ANNUAL. Neither was taken on Meta's word — both were actually run. The modular harness rewrite reproduces the documented seed-42/Full Integration/20yr regression figures exactly and reproduces the direction and approximate magnitude of the v4.8 WEALTH_FLOOR sweep — a genuine, working contribution, though not adopted as the project's harness in this pass (that's a layout decision for the authors, not something a verification pass should decide unilaterally). The living-wage proposal — cutting LIVING_WAGE_ANNUAL from $49,370 to a population-weighted $35,021 — did not hold up: Meta's arithmetic on its own 51-state dataset was correct, but the dataset itself was a 2024 snapshot of MIT's Living Wage Calculator. Fetching MIT's own site directly for its current (February 2026) data build found "1 Adult, 0 Children" figures well above what the 2024 snapshot showed for the same states — California $30.48/hr (was $19.41 in the cited 2024 data), DC $26.72/hr (was $20.80), and even low-cost Mississippi at $20.69/hr (was $14.20). The proposed cut would have replaced a defensible figure with a stale one. Not adopted; a properly current, directly-sourced population-weighted recomputation remains a legitimate future good-first-issue, scoped precisely in CONTRIBUTING.md's Good First Issues.
Also resolved this release: the DOI-archived OSF snapshot's metadata, flagged as an open loose end since v4.8 (it still showed CC BY 4.0 for the whole bundle after the code moved to Apache 2.0), has been updated directly by Duke at OSF to reflect the split — see the footer, above, and CONTRIBUTING.md's License section.
What did NOT get fixed, and why: the
LIVING_WAGE_ANNUAL change proposed by the external audit (deliberately — the evidence points the other direction, see above); adopting the modular harness layout (a real option, not a decision for a verification pass to make); every other open item this audit re-surfaced but did not newly identify (unchanged from v4.8 — see CONTRIBUTING.md's Model Architecture Feedback). Full technical detail in index.html's own changelog and CHANGELOG.md's v4.9 Release Notes.
v4.8Sep 2026UI fixes from direct testing, a licensing decision, and two completed follow-ups from v4.7 (Sep 2026)
Tooltip text running off the browser viewport. Root cause, found by tracing the CSS rather than guessing: the
.abbr-link class was originally written for short (2-4 word) header acronym tips — fixed white-space:nowrap, no width limit — but has since been reused for several full-sentence ⓘ explanatory tooltips, the longest (on the interactive tool's PTF adoption-cap toggle) running to roughly 500 characters rendered as one unbroken line. Fixed by giving .abbr-link the same wrap-and-cap behavior .tip-host (a newer, correctly-built class) already had. Separately, the Scenario Presets grid — two ~130px columns inside a 290px sidebar — centered each tooltip on its button, which needs ~85-95px of clearance on both sides that a 130px column doesn't have; left-column preset tooltips ran off the left edge of the browser window. Fixed with edge-anchored positioning specific to that grid.
Chart-grid CSS that could overflow at non-default browser zoom, and boxes not fitting their content. Several grid layouts used a bare
1fr column track, whose minimum width defaults to its content's own intrinsic size rather than 0 — at some viewport/zoom combinations this can keep a track from shrinking, pushing a chart or table off-screen instead of wrapping it. Changed to minmax(0,1fr) throughout (the standard fix), added min-width:0 to the affected card/KPI elements, and added a defensive canvas{max-width:100%} rule. A second, later-cascading duplicate of one grid rule — without the fix — was found silently overriding the first, correctly-fixed one; both are now consistent.
The BLEI-components chart's
benefitDays line looked missing. Checked directly with a fresh Node.js harness rather than assumed: at seed 42/Full Integration/20yr, cashDays runs 201d→1,915d while benefitDays sits flat at 15.14d the entire run (BU is a fixed monthly allocation, so its BLEI contribution genuinely doesn't grow) — not a computation bug, but a ~100×-larger series squashing two much smaller ones into an indistinguishable sliver on one shared linear axis. Fixed with a dual-axis chart (cashDays on the left, incomeDays/benefitDays on their own right-hand scale), which now shows both smaller series' actual shape, including a real crossover around year 11-12.
System Stability scoring the Traditional Welfare Baseline higher than Full Integration was reported as a possible bug — it is not. This is the coefficient-of-variation behavior already disclosed since v4.2/v4.4 (see Known Limitations): a population pinned at a hard wealth floor has almost no variance left to measure, so it scores as "stable" by this metric's specific definition regardless of whether that flatness is a thriving equilibrium or a floor with nowhere left to fall. No code change; the tooltip fix above should make the existing ⓘ explanation on this exact KPI actually readable in place, which is plausibly why it read as a bug before now.
Source code relicensed to the Apache License 2.0. Both external v4.7 reviews independently flagged CC BY 4.0 as an unconventional choice for software — it has no source-distribution or patent terms — and suggested the common split of an OSI license for code, CC BY 4.0 for papers. Logged as an open governance question in v4.7; resolved this release on Duke's explicit authorization. Apache 2.0 was chosen over MIT for its explicit patent grant and patent-retaliation clause. This page's own content, like all Better To Best Research Hub papers, is unaffected — still CC BY 4.0. The interactive tool's own footer and citation text now disclose the split explicitly. Full license text and split-licensing rationale in CONTRIBUTING.md.
WEALTH_FLOOR sensitivity sweep — the item flagged as blocked-on-a-harness at the end of v4.7. A standalone Node.js harness was built fresh this session (pure functions transcribed directly from index.html) and, before being trusted for anything, validated by reproducing the documented seed-42/Full Integration/20yr regression figures exactly — median wealth $559,223 to the dollar, plus Gini, BLEI, poverty, stability, and EDC all matching too. N=500 seeds, 4 candidate floor values ($0, −$10,000 current, −$25,000, −$50,000), Full Integration and Traditional Welfare Baseline. Finding: Baseline is completely invariant to the floor's exact value on every metric except the trivial fact that its median wealth equals whatever the floor is — once a Baseline agent starts declining nothing ever reverses it, so how deep the floor goes never matters. Full Integration shows a real, modest, monotonic sensitivity (Gini 0.515→0.520, wealth poverty 14.1%→17.5% as the floor deepens from $0 to −$50,000) via a specific, verified mechanism: agents who dip negative and later recover have their BLEI liquid-wealth term clamped to exactly 0 for every year they're underwater, however deep — so a deeper floor doesn't worsen any single year, but does mean more years spent at that clamped zero before finally turning positive. Not acted on: the shipped default stays at −$10,000; this is data for the framework's authors to weigh, following the same "sweep, don't decide" pattern as the existing λ Calibration Status sweep. Full table and mechanism discussion in CHANGELOG.md's v4.8 Release Notes.
Occupation-risk dataset lead — stronger evidence, still one step short of full verification. v4.7 flagged a Kaggle-hosted dataset that might be a complete, machine-readable version of Frey & Osborne's 702-occupation table, without checking completeness or license. This session found independent, non-Kaggle corroboration of the same 702-row count and a third-party dataset card naming the exact needed columns and stating an MIT license — meaningfully stronger than v4.7's open question, but still short of pulling the actual file and diffing it against the paper's own appendix, which needs Kaggle access this session's sandboxed environment didn't have. The "FOWIGS" citation from the second v4.7 ChatGPT pass remains unverified — a fresh search again found no trace of it. Full detail in CONTRIBUTING.md's Good First Issues.
What did NOT get fixed, and why: acting on the
WEALTH_FLOOR sweep's findings (deliberately — see above); pulling and verifying the occupation-risk CSV itself (blocked on access); the v4.6-flagged "no plateau" long-horizon wealth-growth question (still open, needs its own dedicated release); TARGET_WEALTH/TARGET_POVERTY/TARGET_GINI recalibration (still a framework-author decision). Full technical detail in index.html's own changelog and CHANGELOG.md's v4.8 Release Notes.
v4.7Sep 2026dependency and documentation hygiene, prompted by two rounds of external review (Sep 2026)
Chart.js is now pinned with a real, verified SRI hash — closing an item open since v3.8, when two earlier reviewers each supplied a hash "from memory" that proved unusable. This time the hash was computed directly against the actual downloaded npm package (version confirmed via the file's own banner), and — independently — the second ChatGPT pass computed and reported the identical 64-character digest from the same npm artifact, a meaningful cross-check given SHA-384 is a deterministic function of file bytes. Rather than pin that hash against the still-unverified cdnjs URL, the script tag now points at a jsDelivr npm mirror, which serves npm packages byte-for-byte by construction — the pinned hash is valid by construction, not by hope.
A new open calibration finding, logged but not corrected. Agent wealth is drawn lognormal(10.5, 1.2) — now named
WEALTH_INIT_MU/WEALTH_INIT_SIGMA in the engine, same values — with a median of exactly e^10.5 ≈ $36K, matching this framework's own long-standing "Fed SCF 2022" citation. The actual 2022 Survey of Consumer Finances reports median household net worth at ≈$193,000 (Kuhn & Ríos-Rull, NBER working paper, 2025) — roughly 5.3× higher. Both external reviews independently surfaced this; genuinely separate from the wealth-init distribution's already-documented inherent Gini ≈0.60, since Gini of a lognormal is a function of σ alone, with no μ term — correcting the median would move absolute wealth-poverty rates without touching Gini at all. Given how foundational this single draw is to every downstream figure the model produces, fixing it is being treated as its own future mechanics-audit-plus-large-N-restudy candidate, not a value swap alongside a hygiene release — see Known Limitations, below, and CONTRIBUTING.md's Model Architecture Feedback.
PTF's two "reduction" figures, disambiguated. This page's own Key Concepts card and JSON appendix stated PTF establishments "achieve 40–60% overhead reductions" without distinguishing that Mondragon-analogy figure from the 12–16% consumer-facing cost-reduction rate (
PTF_savings_rate) the engine actually implements — the exact pair of numbers CONTRIBUTING.md's own v4.2 changelog already had to disentangle once before in an old README. The first ChatGPT audit cited the 40–60% figure as if it were the simulation's own parameter; both spots on this page now say explicitly that it isn't.
A quick scatter now appears in-app after LHS exports — wealth poverty vs. BU allocation across the 100 sampled design points, reading data already computed for the CSV, with zero new simulation runs or RNG draws. A small UX closure, not a methodology change; the CSV-plus-external-Sobol/Morris workflow is unaffected.
What the audits got wrong, documented rather than quietly ignored: the first ChatGPT pass claimed the interactive tool "only has CSV" export (JSON has existed since v3.3), claimed a "Citation Generator" UI feature that doesn't exist (the footer's citation is a static string), conflated the recession income-shock mechanism with inflation (two separate mechanics), misattributed the $1,200 BU figure to CEX data (it's tied to the Alaska PFD analogue), and recommended splitting the codebase into modules and adding a Dockerfile — both of which either contradict an explicit standing constraint or misdiagnose the project's actual reproducibility risk (cross-browser floating-point drift, not a server environment issue). The second pass, while substantially corroborating the above, cited a "FOWIGS" Frey & Osborne/BLS-OES crosswalk dataset that a direct search could not substantiate — noted as unverified rather than relied upon.
What did NOT get fixed, and why: correcting
WEALTH_INIT_MU itself (needs its own audit-and-restudy pass); a WEALTH_FLOOR/TARGET_* sensitivity sweep proposed alongside this release (blocked on validating a Node harness against the documented seed-42 regression table first, rather than risk a fresh unverified reimplementation); building on the newly-found occupation-risk dataset lead (its completeness and license need direct verification first); and relicensing the code away from CC BY 4.0 (both reviews independently flagged this as unconventional for software and suggested an OSI license for the code specifically — a real, precedented pattern, but a governance decision for this project's authors, not something to change unilaterally). Full technical detail in index.html's own changelog and CHANGELOG.md's v4.7 Release Notes.
v4.6Aug 2026another good-first-issues pass (Aug 2026)
Latin Hypercube sensitivity export implements the exact spec CONTRIBUTING.md had carried as "implementation-ready" since it was first written: a stratified LHS design over the same five parameters OAT already perturbs, reusing
mulberry32 for the design draw, exporting the design matrix plus outcome metrics (poverty, BLEI, participant/non-participant poverty) as CSV. Each of 100 design points runs its own 200-agent/10-year mini-simulation at a fixed seed (matching OAT's own convention of holding the stochastic realization fixed so cross-row variation is attributable to parameters, not RNG noise). Deliberately not a Sobol sampler — genuine Sobol indices need paired Saltelli-structured samples beyond a single LHS design — but well-stratified coverage suited to external Sobol/Morris/regression analysis in Python or R, which is the actual gap the spec described. Verified directly against the shipped functions: all 5 dimensions confirmed properly stratified, and a sample run through the real engine produces poverty/BLEI outputs that track sensibly with the sampled parameters.
Feedback-clamped PTF adoption, opt-in and off by default, addresses the Model Architecture Feedback item flagging that "the PTF slider sets the t=0 adoption probability only... a quota-based or feedback-clamped adoption model remains open." Confirmed the problem directly before fixing it: an uncapped seed-42/20yr Full Integration run reaches 53.4% realized PTF share against an 18% slider, via two independent growth pathways (Bass-diffusion/distress adoption and a separate SZH→PTF induction check). A new toggle — off by default — makes the slider a real ceiling when enabled, checked live and incremented per-agent within the year rather than only once per year, catching a first implementation attempt that still overshot (a once-per-year check alone only cut the overshoot to 23.8%, not the ~18% intended). One honest caveat, caught in testing and documented rather than glossed over: the cap constrains organic growth beyond the year-0 random assignment, not sampling variance already present in that assignment itself — a seed whose initial Bernoulli draw happens to land above the slider (confirmed for seed 42: 95/500 agents = 19.0% at year 0, before any dynamics run) holds at that level rather than being forced down. Implemented as a use-side gate on RNG draws the engine already draws unconditionally every year (the v4.3 RNG-coupling discipline), so enabling the cap adds no new RNG call and every existing preset's regression baseline is unaffected.
FBS₅₀ half-saturation bandwidth — CONTRIBUTING.md's own λ Calibration Status note (Mathematical Framework §4, above) had already hand-computed this figure in prose (≈$524–$4,191/month) as the recommended reparameterization target; it's now a named, derived constant (
CFG.FBS_HALF_SAT_LO/HI = ln(2)/λ) displayed directly in the interactive tool, so it can't drift out of sync if the λ range is revised again. Reporting change only — per-agent λ sampling, RNG draws, and every simulation output are unaffected; a genuine reparameterization (sampling FBS₅₀ as uniform and deriving λ from it) would change the shape of the λ distribution and remains a distinct, larger open item.
The near-poverty cohort's 40-year extension, unreconfirmed since v4.1 (flagged in every release since as progressively more stale), was rebuilt under current mechanics: N=1,000 seeds (a fresh draw, matching v4.1's own methodology), Full Integration, 500 agents, 40-year horizon. The milestone question v4.1 originally asked is now moot — v4.3's wealth-loop fix already answers it inside the existing 20-year table (median 13.0 years to Flourishing), and this rebuild reconfirms that almost exactly at N=1,000 (median 13 years, mean 13.4 years; Stable and Secure milestones both within a percentage point of the 20-year figures). What the 40-year window actually adds is a different and more consequential finding: no plateau. Cohort median BLEI keeps compounding at an accelerating pace past year 20 — 1,618 days at year 20 to 7,058 days by year 40, more than quadrupling — and population-wide median wealth reaches roughly $2.1 million by year 40 with no sign of a ceiling. See Performance Comparison, below, for the full table and Known Limitations for the open question this surfaces about long-horizon wealth growth.
What did NOT get fixed, and why: another good-first-issues pass by explicit scope. Occupation-stratified automationRisk,
WEALTH_FLOOR/TARGET_* recalibration, the framework-level low-wage-mechanism question, and a genuine production/treasury constraint remain open, unchanged from v4.5. Two items got real research rather than being left untouched, though: occupation-stratified risk is now scoped to a specific source (Frey & Osborne's own appendix publishes per-occupation probabilities for all 702 SOC codes — what's missing is transcription and a BLS employment-share crosswalk, not new data), and a search for a published population-weighted LIVING_WAGE_ANNUAL figure came up empty (MIT's calculator has no single national aggregate to retrieve) — a negative but informative result, documented rather than left as an unexamined todo. Full technical detail in index.html's own changelog and CHANGELOG.md's v4.6 Release Notes.
v4.5Aug 2026a good-first-issues pass, not a mechanics audit (Aug 2026)
The PTH liquidity haircut finally gets its variable schedule. v4.3 added per-agent
pthTenure tracking as a first step; v4.5 implements the haircut itself — a linear ramp from 15% at tenure=1 (the closest available point to the BLEI paper Table 1a's "6 months" anchor, given tenure is tracked in whole years) to 85% at tenure≥5 (the paper's "5+ years" anchor), replacing the flat 50% share of annual Acre Equity appreciation counted as liquid wealth used through v4.4. Both anchors are the midpoint of the paper's cited range (10–20% / 80–90%) — the paper gives two qualitative points, not a closed-form curve, so the ramp between them is a documented interpretation choice, not a re-derivation (see Step 1 card and Mathematical Framework §2, above). Since PTH membership doesn't change once assigned, most PTH agents spend the majority of a 20-year run at the mature 85% rate — higher than the old flat 50% for 16 of 20 years, lower only in years 1–4. Isolated effect, N=300 seed-paired comparison (same population/RNG per seed, only this mechanism toggled): median wealth $523,958→$526,941 (+0.6%), median BLEI 1,820.6d→1,826.0d (+0.3%); Gini, wealth poverty, BLEI poverty, and flourishing rate unchanged to the precision reported. Seed-42 Full Integration reference: median BLEI 1,951d→1,965d, median wealth $558,001→$559,223. This is a scoped sanity check, not a fresh N=5,000 study — the KPI row and large-N table elsewhere on this page remain the v4.4 figures; see the v4.5 banner near the top of this page. All six validation checks pass post-change.
Three smaller good-first-issues, done alongside it. "Copy parameters as URL" serializes the current sliders/toggles/seed into a query string, copied via the Clipboard API; loading a shared link applies its params on top of the Full Integration defaults before the page's own auto-run, so the seed-42 "illustrative reference run" banner only fires when no recognized params are present. Social Security-anchored cohorts (real since v4.3, harness-only until now) are surfaced as a final-year table in the interactive tool's own results panel — SSI-level, SSDI-level, and Retirement-level rows, computed from the same final agent state and metric functions the headline KPIs already use, cross-sectional by design like the harness study itself. Hover tooltips now show BLEI tier day-ranges next to the tier-distribution chart and a one-sentence description of what each preset button actually configures — caught and fixed a real interaction bug along the way, where the preset "active" styling was silently overwriting the new tooltip's CSS class on every click.
What did NOT get fixed, and why: this was a good-first-issues pass by explicit scope, not an audit release.
WEALTH_FLOOR and TARGET_* recalibration, the Sobol/LHC sensitivity export, occupation-stratified automationRisk, the near-poverty cohort's stale 40-year extension, and the framework-level low-wage-mechanism question all remain open, unchanged from v4.4. Full technical detail in index.html's own changelog (search "v4.5") and CHANGELOG.md's v4.5 Release Notes.
v4.4Aug 2026two independent adversarial audits, and the fixes they confirmed (Aug 2026)
Wage/income scale unified across BLEI, FBS, and Gini. v4.0 had fixed unit-mixing inside these formulas using
SIU_TO_USD (≈16.63) — a cost-side-derived approximation, invented because no real wage-side anchor existed yet. v4.3's WAGE_TO_USD (≈100.52, Census/BLS-sourced) was scoped to the main wealth loop only, leaving the same agent's wage worth roughly $3,518/month for wealth accumulation and roughly $582/month for welfare measurement simultaneously — a ~6× divergence the audit correctly identified as no longer defensible once one of the two anchors is empirically grounded and the other isn't. SIU_TO_USD is retired; agentEDC() was and remains unaffected, since it's a dimensionless SIU/SIU ratio that never used either constant. This is not a repeat of v4.3's compounding-loop risk (these formulas recompute fresh each year), but it does feed back into wealth accumulation indirectly through the FBS advancement gate, which needed a compensating adjustment: FBS_LAMBDA, left unscaled after its dominant input (Yusd) grew ~6×, pushed advancement probability toward saturation regardless of BU level and measurably weakened the BU-monotonicity validation check. Correction (post-release review): this document previously described the FBS_LAMBDA rescale as a "dimensionally-necessary consequence" of λ's 1/USD units — that overstated the case. FBS mixes the wage-derived term that changed scale with fixed-dollar terms (BU face value, basic cost) that did not, so FBS itself does not scale by a single clean factor, and dividing λ by exactly 6.0456 is a chosen behavioral recalibration to restore useful dynamic range, not a unit identity. See the open calibration-status note under Mathematical Framework §4 for the full, corrected account, including why the original range saturates and why the rescaled range should not itself be read as empirically validated.
True common-random-numbers pairing, at last, across the full three-way comparison. v4.2 paired the Baseline/CCO-Only/Main populations but left their year-by-year trajectories on three independently-offset RNG streams — decorrelated from each other and from Main, not just from population construction, exactly as v4.2's own note above already flagged as a remaining gap. Since v4.3's
runYear() fix made every scenario draw the same count and order of random values regardless of policy toggles, three independent same-seed generators now necessarily produce identical draw sequences at each (agent, year) position — isolating the policy effect from Monte Carlo noise, which is what CRN pairing is for. Verified to have zero effect on Main's own single-scenario trajectory.
An unexplained baseline initial-wealth penalty, and an exogenous-conditions asymmetry, both removed. Baseline agents received an extra ×0.85 wealth adjustment at construction, on top of sharing v4.2's paired latent population — applied before CCO has had any chance to act, embedding part of the treatment effect into the counterfactual's own starting point. Removed; no externally-sourced justification existed for the specific figure. Separately, Baseline was hardcoded exempt from AI automation exposure even when a user enabled it for the tested scenario, while CCO-Only already correctly mirrored the setting — now paired. Neither fix meaningfully changes the standard reference-configuration figures on this page (automation defaults off; and Baseline's median wealth remains exactly floor-pinned regardless — now confirmed in 100% of N=5,000 runs rather than "more than half," since the binding constraint turned out to be the ongoing annual cost/wage gap under 20 years of compounding, not the year-0 starting point).
A genuine bug, found independently of either audit while building this release's large-N harness. The general-purpose gamma-distribution sampler divides by
(a−1) inside a log term; at a=1 exactly this is x/0, making the acceptance test 0×log(∞)=NaN and therefore always false — every call exhausted all 5,000 rejection iterations and silently returned the constant 1, not a random Exponential(1) draw (confirmed: 20 of 20 identical samples in testing). This directly narrows drawAutomationRisk()'s two component distributions, used once per agent on every simulation run, and wasted the full 5,000 dead iterations each time in the process. Fixed with the exact closed-form solution — Gamma(shape=1) is exactly Exponential(1) — which also produced a roughly 200× population-construction speedup as a side effect, without which this release's fresh N=5,000 study would not have been practical to run in the time available.
Full N=5,000 re-study, with a methodological correction worth naming. Figures throughout this page above reflect the fresh study (Full Integration median wealth $526,120, up modestly from v4.3's $495,411 — most of v4.3's much larger jump came from the wealth-loop fix itself, not this release's formula-unification work; BLEI poverty fell from 17.1% to 12.5%; participant poverty fell from 15.4% to 10.0% while non-participant poverty stayed exactly unchanged at 34.3%, a clean mechanistic confirmation that non-participants never execute the FBS-gated code path this release touches). The Social-Security-anchored cohorts (v4.3, above) improved measurably — SSDI-level median BLEI 16.4d→22.6d, retirement-level 16.6d→a median-of-medians 25.1d — but an initial mean-based reading of the retirement-level cohort would have overstated this as crossing the 30-day Threshold; BLEI is right-skewed for these small (≈4–86 agent) cohorts, and the mean (32.0d) sits above the line while the median-of-medians (25.1d) and the actual per-seed crossing rate (31.1% of seeds) both show the typical case still falls short. Caught before publication, corrected throughout this page and CONTRIBUTING.md — reported here because getting this distinction right mattered for what the framework can honestly claim, and because a large-N study is only as trustworthy as the statistics used to summarize it.
What did NOT get fixed, and why:
WEALTH_FLOOR, the pre-existing wealth/poverty/Gini design targets, and a genuine production/treasury constraint on BU conversion remain open — all explicitly flagged in CONTRIBUTING.md as framework-level decisions requiring the framework's own authors' judgment, not code fixes; this release adds a direct in-app disclosure on the affected KPI badges (see the interactive tool) without picking new numbers. The v4.1 40-year cohort extension remains unreconfirmed, now three mechanics-changing releases stale — given how far the 20-year figures have moved, a fresh 40-year run would very likely show materially different results, tracked as an open item rather than assumed. Full technical detail, including exact line-level rationale for every change, is in index.html's own changelog (search "v4.4") and CONTRIBUTING.md.
v4.3Aug 2026main wealth-loop unit fix — the highest-priority open item since v4.0, and not a clean win (Aug 2026)
WAGE_TO_USD (≈100.52, derived from $42,220 median personal income, Census/BLS CPS ASEC 2023) and LIVING_WAGE_ANNUAL ($49,370, a national living-wage figure from World Population Review's MIT-sourced state table), replace netting a wage figure against BASE_DAILY_COST — a bare-subsistence floor, correct for BLEI's poverty-line use but wrong for a 20-year compounding loop netted against real wage, which produced an ~11× wealth explosion and broke BU-monotonicity on 2 of 5 seeds when tested with the wrong anchor. All six validation checks pass under the shipped anchor.
This is not a clean win — it redistributes as well as it grows, along more than one axis. At N=5,000: Full Integration median wealth rose from $80,008 to $495,411 (≈6.2×), but EDC-adjusted Gini rose from 0.479 to 0.536 and wealth poverty rose from 14.4% to 19.5% — low-wage-draw, high-automation-risk, and non-participating agents don't share proportionally in the wage-side correction the way median-and-above-wage agents do. The Traditional Welfare Baseline collapsed far more severely: with no CCO/PTF/PTH cost relief, its full weight lands on the new, roughly-doubled cost anchor — median wealth fell from $7,316 to exactly the wealth floor, −$10,000, meaning more than half the baseline population was pinned at the debt ceiling by year 20 for the first time, wealth poverty rose from 52.2% to 72.3%, and Gini rose from 0.800 to 0.855. Meanwhile the near-poverty cohort (low starting wealth, not low wage — most members have ordinary wage draws) reversed completely: cohort-median time to Stable dropped from 158.8 to 48 months, and the cohort reached Flourishing in 5,000 of 5,000 runs within the 20-year horizon, versus 0 of 5,000 even after extending to 40 years under pre-v4.3 mechanics. Together these say something more precise than "wealth up, Gini up": the fix helps agents by wage level, not by starting wealth.
A new Social-Security-anchored cohort study, prompted by that finding, is the sharpest test of the framework's poverty-elimination claim run so far — and the framework does not pass it. Comparing Compassionism to the US baseline using median-income anchors doesn't show how the framework treats the population most relevant to a poverty-elimination claim: people on a fixed, sub-median income, not median-wage workers. Real 2026 SSA figures (SSI, SSDI-average, average retired-worker benefit) tag three cohorts by initial wage via
WAGE_TO_USD. Full Integration helps this population enormously in relative terms — 20–30× the baseline's BLEI, where these agents are functionally at zero — but median BLEI in the teens of days remains deep in the Precarious tier, nowhere near the 30-day Threshold, let alone the reference configuration's own 67.8% population-wide Flourishing rate. Full table in CONTRIBUTING.md; the underlying mechanism is direct — even with PTF/PTH cost reductions and BU support, an agent whose wage sits near or below Social-Security-benefit levels doesn't generate enough annual surplus for the model's other mechanisms to compound meaningfully within 20 years.
Three smaller fixes shipped alongside the headline change. The
runYear() RNG-coupling fix: every conditional quantity in the per-agent loop (BU spend fraction, CIP quality bump, octave advancement, SZH→PTF induction, PTH appreciation noise, PTF Bass adoption) is now drawn unconditionally every year for every agent, mirroring v4.2's population-construction fix — only the use of the drawn value is gated. This let the non-participant-poverty validation check tighten back to strict per-seed agreement at n=200 (was a mean-across-5-seeds workaround at n=500, adopted specifically because of this coupling gap — see v4.2's note above). A bimodal automationRisk distribution replaces the previous uniform[0.2,1.0] — a 47%/53% high-risk/low-risk mixture sourced to Frey & Osborne (2013) and Autor (2015). Per-agent PTH tenure tracking is added as a first step toward the variable liquidity haircut this page's Step 1 card already flags as unimplemented — tracking only this pass, the haircut formula itself remains flat 50%.
What did NOT get fixed, and why: the
WEALTH_FLOOR of −$10,000 was designed for occasional insolvency, not a scenario where it binds for the median of an entire population — whether it's still the right value now that the cost anchor it's measured against roughly doubled is flagged as an open question this release surfaces, not resolves; a genuine aggregate production/treasury constraint on BU conversion proceeds; whether a mechanism specifically targeting low-wage (not just low-wealth) populations is a gap the framework's design should address. All tracked in CONTRIBUTING.md's Model Architecture Feedback, which is where this page's own reconciliation notes above point for the pre-existing Gini and target-calibration questions too. This page's own sync to v4.3 was deliberately deferred pending an independent adversarial audit of the release — that audit, and v4.3's resulting figures, are folded into the v4.4 entry below, since no intermediate v4.3 snapshot of this page was ever published.
v4.2population-pairing & validation improvements (external audit + internal review, Aug 2026, cross-checked against CONTRIBUTING.md's Reproducibility Testing and Model Architecture Feedback)
Common-random-numbers population pairing. The main run, "CCO Only," and baseline comparisons now share one canonically-drawn latent population instead of three independently-sampled ones — closing the counterfactual-isolation item from the external review this page's v4.0 note already discusses ("the baseline and CCO-only populations are not actually the same individuals"). The earlier objection that pairing "isn't well-defined once maxOct/maxMult differ in support between scenarios" holds against the scaled outcome (octave/quality) but not the underlying draw — the pre-policy shape variate is now shared, then scaled independently per scenario's own ceiling, which is what a genuine "same individuals, different policy" comparison needs. Implementing this surfaced an independently-found bug:
makeAgent()'s three eligibility draws used short-circuit &&, so the number of RNG calls consumed during agent construction depended on which subsystems were toggled on — two scenarios built from the "same" seed but different toggle states (e.g. the validation suite's PTF-on vs PTF-off check) silently diverged in every draw after the first toggle difference, not just membership. Now drawn unconditionally. This does not extend to full within-run trajectory pairing — runYear() still consumes a variable number of RNG calls per agent depending on which of its own conditional branches fire each year (participants trigger more draws than non-participants), so two scenarios with different participation composition still decorrelate agent-level trajectories after year 0, even from an identical starting population. Aggregate comparisons (poverty rate, median BLEI) remain valid; a fully-paired per-agent comparison would need every RNG-consuming branch in runYear() drawing unconditionally too — a materially larger change, tracked as an open item in CONTRIBUTING.md, not attempted here.
Structural System Stability metric. Replaces the v3.6-flagged trend-plus-noise heuristic (
stabBase = improving ? 0.88 : 0.60, plus a bounded random noise term) with the inverse coefficient of variation of median wealth and median BLEI over the run's final quarter — no RNG call, no manual per-mechanism bonus. A mechanism that genuinely stabilizes outcomes now shows up endogenously as lower volatility, since every mechanism already feeds wealth and BLEI within runYear().
Validation suite: multi-seed robustness plus two new checks. The original four checks each ran once, on one hardcoded seed apiece — unable to distinguish a genuine structural property from a lucky draw. All four now run across five seeds and require unanimous agreement (all four are 5/5 robust). Two checks were added: non-participant poverty doesn't worsen when CCO is enabled (isolates CCO status alone, holding every other toggle fixed; needed a larger population and a mean-across-seeds criterion rather than per-seed agreement, because of the within-run RNG-coupling property described above — at the suite's usual 200-agent size this check only agreed 2–3 times in 10 seeds tried during development, which was noise rather than signal; at n=500 every seed and the mean favor non-participants doing slightly better under CCO, not worse), and a structural-invariants check (population conservation across BLEI tiers, octave/wealth-floor/participation bounds, Gini bounds) as a regression net for future changes. The suite is renamed "Internal Consistency & Behavioral Test Suite" and labeled against an ODD-style Level 1–5 validation hierarchy — this suite covers Levels 1–3 (code correctness, mathematical invariants, behavioral validation) only; Levels 4–5 (external empirical validation) remain open, per Known Limitations.
What did NOT get fixed, and why (carried forward from v4.0's note, above, still current): the main wealth loop's unit simplification; a genuine aggregate production/treasury constraint on BU conversion proceeds; a shared parameter-builder across
getCurrentParams()/OAT/validation (still an accepted, if imperfect, duplication). All tracked as open items in CONTRIBUTING.md.
Large-N reconfirmation (Aug 2026) — complete. v4.1 could verify its large-N figures were unchanged by inspection alone, because none of its fixes touched
makeAgent(), runYear(), or calcMetrics(). v4.2's population-pairing fix touches makeAgent() directly, so that shortcut wasn't available — a full N=5,000 study was run instead (same methodology as the v4.0/v4.1 studies: a Node.js harness driving the shipped engine functions directly — makeLatentPopulation(), instantiateAgent(), runYear(), calcMetrics(), bleiMetrics(), structuralStability() — seeds 1–5,000, 500 agents, 20 years, shock disabled to match the Full Integration reference preset; full figures in Performance Comparison, below). Headline result: figures across all three scenarios land within a few hundredths of a percentage point of the v4.0/v4.1 numbers they replace — Full Integration Threshold Rate 92.1% (was 92.1%), BLEI Poverty 7.9% (was 7.9%), EDC-adjusted Gini 0.4843 (was 0.484), Median Wealth $81,597 (was $81,629); Traditional Welfare Baseline reconfirmed just as tightly (Threshold Rate 47.8%, Gini 0.8003, both effectively unchanged). This confirms the fix changes which specific agents get compared, not the model's aggregate behavior — exactly what a counterfactual-isolation correction should do.
A genuine methodology finding along the way, for anyone reproducing this study. An initial CCO-Only run, using the CCO-Only preset's own 75% participation figure, diverged from the documented v4.0/v4.1 numbers by 1–2 percentage points across poverty, wealth, and Gini — consistently in the direction lower participation would produce. Baseline, run the same way, matched immediately (its parameters are fully hardcoded, independent of whatever scenario it's being compared against), which isolated the gap to participation rate rather than a deeper problem: the interactive tool's own internal "CCO Only" comparison (
pCCO in simulate()) inherits its participation rate from whichever scenario it's paired against — 78%, the Full Integration reference preset's value — not from the CCO-Only preset's own standalone 75%. Re-run at 78%, CCO-Only also reconfirms closely (Threshold Rate 90.3%, was 90.3%; EDC-adjusted Gini 0.5164, was 0.516). Documented here because it isn't obvious from the preset definitions alone, and would silently reproduce the same 1–2pp gap for the next person who runs this study starting from PRESETS.cco directly.
What changed as expected, by construction, not by drift. System Stability now reports the new structural metric — Full Integration 94.7%, CCO-Only 95.3%, Traditional Welfare Baseline 80.6% — not comparable to the old heuristic's numbers, which is exactly what a redefined metric should produce. The near-poverty cohort's median time to the Stable tier is now 158.8 months / 13.2 years (was ~165 months / 13.7 years) — a genuine, modest shift (95% CI ±0.4 months on N=5,000, so not sampling noise), consistent with a slightly different random population per seed under the new RNG threading. The cohort's Threshold crossing is better described as immediate (month 0, from initial liquid wealth alone, in 5,000/5,000 runs) than the previous "~12 months" — this is a measurement-granularity refinement, not a changed finding: this document's own prior wording ("essentially immediate — driven by initial liquid wealth, not simulated improvement") already said as much, the new methodology just adds a pre-dynamics data point to confirm it. Flourishing remains unreached by the cohort median within 20 years (0 of 5,000 runs, same as before).
What did NOT get fixed, and why (carried forward from v4.0's note, above, still current): the main wealth loop's unit simplification; a genuine aggregate production/treasury constraint on BU conversion proceeds; a shared parameter-builder across
getCurrentParams()/OAT/validation (still an accepted, if imperfect, duplication). All tracked as open items in CONTRIBUTING.md. One figure on this page was deliberately not re-run: the v4.1 40-year Flourishing-horizon extension (N=1,000) remains a v4.1-mechanics result — that specific reconfirmation is tracked as a separate open item rather than assumed. Full seed-42 single-run figures (distinct from this large-N study) are in CONTRIBUTING.md's Reproducibility Testing table.
v4.1audit-driven bug fixes (internal Claude/Anthropic chat audit, Aug 2026)
PTF attribution leak in the ablation feature. v4.0's paired-population ablation redesign (above) intentionally builds every removal scenario's agents under the full config, so membership flags (inCCO/inPTF/inPTH) match across scenarios for RNG-pairing — correct for the simulation mechanics, which already re-check the relevant system toggle everywhere they consume those flags. But
agentBLEI()'s SZH+PTF cooperative-synergy term trusted a.inPTF directly, with no ptfOn parameter to check against — unlike ccoOn/pthOn, which already gate the equivalent CCO/PTH terms everywhere they're used (re-audited in this pass and confirmed unaffected). Result: the "Remove PTF" ablation result could retain a synergy bonus it shouldn't have whenever SZH was also active, understating PTF's true contribution on the Attribution chart — worth roughly 12 BLEI days per affected agent in testing (validated with a Node.js harness against the actual shipped functions, isolating the leak in a single agent and confirming it disappears with ptfOn correctly threaded through, with zero change to normal non-ablation runs). Fixed by adding ptfOn to agentBLEI()/bleiMetrics(), mirroring how pthOn already works. If you've cited or archived Attribution-chart output generated under v4.0 with SZH active, it's worth a re-run under v4.1.
Illustrative reference-run label was never shown.
IS_REF_RUN was set true by the page-load auto-run (v3.4) but nothing ever read it, so the "📊 Illustrative reference run — seed 42…" banner stayed hidden permanently since its introduction — the general-purpose exploratory-simulation notice nearby likely covered for it, which is probably why six subsequent external audits (v3.6 through v4.0) missed it. Wired up: the flag is read once, to show the banner only for the genuine auto-loaded run, then reset so it can't linger into a later manual run.
Stale UI hint text corrected against current formulas. Several small strings had drifted from the mechanics they describe — the same class of gap the v4.0 review caught for the recession-mode figure below, just missed by that pass. The interactive tool's recession hint stated a 2–4 year / 65–85% income-multiplier range; the actual beta(5,2) formula (unchanged since v3.3, and correctly described in this document's own Mathematical Framework §7) produces 1–3 years / 70–95% — this document's own Step 5 card had the same "2–4 years" error, now corrected below. The Max Octave slider's hint described conversion capacity as an exponential "C = C₀ × 2ⁿ"; the actual v4.0 mechanism is linear in octave/maxOct, reaching exactly the quality-multiplier ceiling at max octave (an exponential form would hit 512× at octave 9, far past that ceiling) — the hint now describes the real shape. A static default display value read "14.58×" against the correct "14.56×" (9×1.618); cosmetic; it self-corrects the instant the reference preset auto-loads, but was wrong in markup. A chart caption asserted baseline recessions are unconditionally "enabled," no longer accurate once v4.0 paired baseline's shock exposure to the user's own toggle. OAT sensitivity's PTF-share perturbation used a slightly wrong slider range. None of these affect any computed number anywhere in this document or the interactive tool — full detail in
index.html's own changelog (search "v4.1").
Large-N reconfirmation, and a genuine extension. Because none of the above touches
makeAgent(), runYear(), or calcMetrics(), the v4.0 large-N figures throughout this page should be numerically unchanged under v4.1 — verified, not assumed: an independent N=1,000-seed sweep of all three scenarios (Full Integration, CCO-Only, Traditional Welfare Baseline) landed within statistical noise of the v4.0 5,000-seed figures on every metric checked (BLEI poverty, EDC-adjusted Gini, median wealth; near-poverty cohort size, and its Threshold/Stable milestone timing, reconfirmed almost exactly). Rather than stop at reconfirmation, the near-poverty cohort tracking (Performance Comparison, below) was extended to a 40-year horizon (N=1,000, Full Integration) — the specific follow-up the v4.0 note above flagged as open. The cohort's median member still does not reach Flourishing by year 40 (0 of 1,000 runs), though its median BLEI keeps climbing throughout (≈135 days at year 15 → ≈428 days at year 40) rather than plateauing — full trajectory and discussion in Performance Comparison.
v4.0mechanism alignment, dimensional coherence & statistical design (external Refine.ink review, Aug 2026 + internal audit against the BLEI paper's own §8 Table 6 spec)
Mechanism gaps closed — several of this page's own step descriptions above (Step 3 FBS gating, Step 4 network-density θ) described the intended design accurately but did not match the shipped engine, which the external review caught: octave advancement now genuinely runs P(advance)=1−exp(−λ·FBS) instead of a fixed-probability heuristic gated on BLEI>30 days; octave now governs CCO conversion-rate capacity (previously it had no role in conversion at all — only quality did); a documented 25% share of PTH's annual housing saving now builds Acre Equity instead of 100% landing as direct cash relief; SZH's synergy coefficient θ is now genuinely gated below 55% PTF density instead of applying linearly at every density; PTF adoption gained the Bass-diffusion imitation term the ODD protocol already claimed it had.
Dimensional coherence — BLEI, FBS, and the (now-implemented) EDC-adjusted Gini convert the simulation's internal wage unit (SIU) to USD via a single derived constant before mixing it with dollar-valued terms, closing a unit mismatch the review identified. The main wealth-accumulation loop deliberately keeps its pre-v4.0 SIU/USD simplification — see §2's note in the Mathematical Framework section above for why a full fix there was built, tested, and not shipped (it proved extremely sensitive to the conversion constant over multi-decade runs).
Counterfactual isolation — the baseline and "CCO Only" comparison populations are now generated independently from their own scenario parameters instead of inheriting octave/quality drawn under whichever configuration the user is currently testing; baseline's shock exposure and inflation are now paired to the user's toggle / anchored to the documented 3% CPI respectively (previously hardcoded); when shocks are enabled, all three trajectories (baseline, CCO-only, main) now draw from the same recession-timing sequence.
Validation & statistics — two validation-suite checks (participation threshold, PTF/inflation dampening) previously passed while silently testing nothing, due to a parameter-name/units mismatch between the test definitions and the engine; both fixed. The 3×/10×/50× Monte Carlo controls were completely non-functional — a flag-clearing bug meant every multi-run request silently executed exactly once with zero results collected; fixed. Ablation/attribution now uses a paired population and paired RNG stream (previously each removal drew a fresh sample against a different seed, diffed against an unrelated main run), with signed deltas — a subsystem whose removal improves BLEI now shows as negative instead of being clamped to zero. OAT sensitivity now perturbs all five parameters by a uniform ±20% of each parameter's own slider range, replacing a mix of relative and fixed-percentage-point steps.
What did NOT get fixed, and why: the main wealth loop's unit simplification (above); a genuine aggregate production/treasury constraint on BU conversion proceeds (v4.0 adds transparency tracking only, not a capacity constraint); a structural System Stability metric (still the v3.6-flagged trend heuristic — resolved in v4.2, below). All three are tracked as open items in CONTRIBUTING.md rather than silently left undocumented.
Headline statistics on this page are now v4.0-native, from a fresh large-N study. Every figure below — KPI banner and Performance Comparison — was recomputed under the corrected v4.0 mechanics via 5,000 independent seeded runs each of Full Integration, Traditional Welfare Baseline, and CCO-Only (seeds 1–5,000, 500 agents, 20 years; script and raw output available on request via a GitHub issue). This is a change of methodology from the prior "10,000 simulation runs" claim — that figure was never fully documented (batch size, seed range, and code path weren't specified) and, for the EDC-Adjusted Gini figure specifically, could not have reflected shipped code, since EDC-adjusted Gini wasn't computed by the engine until v4.0. The new study reports its exact N, seed range, and 95% CI on the mean of each statistic across runs (not to be confused with per-agent population spread within a single run). Two headline figures could not be recomputed and are marked accordingly below: EPPM and Work Incentive Preservation are not computed by the simulation engine (they're paper-level calculations only — see the BLEI paper). One figure changed substantively in a way worth flagging on its own: the near-poverty cohort's median member does not reach the Flourishing tier (≥730 days) within the 20-year reference horizon in any of the 5,000 runs, once advancement is genuinely FBS-gated rather than a fixed-probability heuristic — see the Performance Comparison note below for what we report instead and why. Regression baseline (seed 42, Full Integration, 20 yr) output is recorded exactly in CONTRIBUTING.md and is not identical to v3.9 — this release changes simulation behavior by design.
v3.9numerical edge cases (external Gemini review cross-check + internal verification)
p10>0 to p10>$100. OAT params mutation: runOATSensitivity() mutated the object returned by getCurrentParams() directly (base.nAgents=200;base.years=10) rather than copying it; harmless in practice since nothing else held that reference, but now builds a fresh object via Object.assign instead. The review also proposed Object.freeze()-ing getCurrentParams()'s return value to prevent slider-change race conditions during an in-flight run — investigated and not applied: runSim() doesn't call getCurrentParams() at all (it builds its own separate params object, a documented duplication rather than a bug), so there was no race condition to fix, and freezing would have silently broken the OAT override this release fixes more directly. The review's supplied Chart.js SRI hash is invalid — a correct sha512 digest is 88 base64 characters, the supplied string is 65 — and was not used; SRI-pinning remains worth doing with a hash generated and verified against the deployed file, per the same conclusion reached auditing v3.7 → v3.8. The review's synchronous-batch-loop concern for runMulti/runOATStep/runAblation doesn't hold either: all three already yield via setTimeout between iterations. Regression baseline (seed 42, Full Integration, 20 yr) produces identical numerical output to v3.8 except the ratio KPI display threshold described above.
v3.8RNG rigor (external Grok review cross-check + internal verification)
years or nAgents therefore shifted the random sequence the main run actually saw, even though the tool's reproducibility claim is "same seed + params → identical run." Each comparison run now executes on its own stream derived from the user seed (mulberry32(seed+700001) / mulberry32(seed+700002)), restored before the main trajectory begins — the same pattern already used to isolate the ablation-attribution runs. Unseeded (Math.random()) runs are unaffected, since no reproducibility claim applies there. The review also raised points that do not hold against the actual v3.7 implementation — participant/non-participant KPI paths already guard against empty arrays, the internal validation suite already exists (four consistency checks), and OAT sensitivity already documents itself as first-order-only — these were verified and not acted on further. A vendored/SRI-pinned Chart.js was considered but not shipped this release: cdnjs's own auto-generated SRI hashes have a documented history of not matching the served file, and a wrong hash silently breaks every chart for every user — worse than the current unpinned load. Regression baseline (seed 42, Full Integration, 20 yr) produces identical numerical output to v3.7, since baseline/CCO-only run length is unchanged for that configuration.
v3.7accessibility & discoverability (external Meta AI review cross-check + internal verification)
DOMContentLoaded and load listeners both called applyPreset('reference'), silently re-applying default parameters a second time before the first run executed. The load listener now only sets the reference seed/flag and runs. (2) Chart accessibility: all 11 Chart.js canvases previously had no accessible name; added role="img" and a descriptive aria-label to each. (3) In-app glossary: added a collapsible acronym glossary above Scenario Presets in the interactive tool, mirroring the Key Concepts accordion on this page. The external review also raised several points that do not hold against the actual v3.6 implementation — RNG is already a seeded mulberry32 generator (not Math.random()), error handling already wraps simulation execution in try/catch, and the year-loop already yields per year rather than blocking — these were verified and not acted on further. Regression baseline (seed 42, Full Integration, 20 yr) produces identical numerical output to v3.6.
v3.6UX & transparency (external Kimi technical audit + internal review)
t=0 adoption probability (ptfShare); independent per-agent adoption checks each year mean realized final PTF share can and does exceed the slider value. UI caption, CSV export column, and run-configuration summary updated to match. (2) System Stability disclosure: added an info tooltip and a Known Limitations entry stating plainly that the displayed value is a trend-plus-noise heuristic (stabBase by poverty-trend direction, + CIP bonus, + bounded random noise) rather than a structural metric derived from wealth variance or BLEI-tier persistence. A structural replacement is scoped for v4.0. (3) Mobile table overflow fixed: removed a hardcoded min-width:640px that forced every results table into horizontal scrolling on mobile viewports regardless of actual content width. (4) Cancel control: single runs and chained Monte Carlo batches (3×/10×/50×) can now be interrupted mid-run without a page refresh. (5) Implicit global declared: IS_REF_RUN, previously assigned without a prior declaration inside the page-load listener, is now an explicit module-level variable. Regression baseline (seed 42, Full Integration, 20 yr) produces identical numerical output to v3.5.
v3.5version & citation consolidation
optimal preset key was renamed reference to match the "Reference" terminology already shown in the UI (see v3.4 framing note below). (4) Effective-max UI indicator: the Quality Multiplier Ceiling slider now displays the Phi-adjusted effective maximum live (e.g. "Effective max with Phi: 14.58×"). Regression baseline (seed 42, Full Integration, 20 yr) produces identical numerical output to v3.4.
v3.3methodology additions
v3.1calibration note
How the entries above were counted
The findings, chart by chart#
Every result of the current release, with what each chart shows and does not show, and every table with downloads: Open the findings explorer
Appendix A: Calibrated Parameters Dataset — v3.3#
unit_system_note.sim_cost_scale field and automation_risk distribution both describe mechanics superseded in v4.3 (see Mathematical Framework §2, §3), its monte_carlo_ci description (a fixed z=1.96) is superseded by Student's t as of v4.15, and its unit_system_note.wealth field's "Fed SCF 2022" citation for the $36K median has not been checked against the actual SCF figures — see the Known Limitations entry on this, added in v4.7, for what a check turned up. Preserved as historical/illustrative rather than rewritten field-by-field — see index.html's own CFG object and CONTRIBUTING.md's Calibration Validation table for the current, authoritative parameter set.
Complete JSON dataset with v3.3 calibrated parameters. v3.1 additions: annual expense system, wealth floor, heterogeneous AI automation risk, PTF as cost-reduction mechanism, BLEI poverty metric, incomeMultiplier recession field. v3.3 additions: wage diminishing returns, beta(5,2) recession distribution, SIM_COST_SCALE rename, Gini zero-wealth fix, participant stratification, computed OAT sensitivity, validation suite, 50× Monte Carlo CI.
Calibrated parameters dataset (JSON, v3.3 snapshot)
{
"simulation_metadata": {
"version": "3.5.0",
"doi": "https://doi.org/10.17605/OSF.IO/QWTE2",
"last_updated": "2026-06-30",
"calibration_iterations": 10000,
"confidence_level": 0.95,
"primary_welfare_metric": "BLEI (temporal stability in days)",
"secondary_poverty_metric": "BLEI poverty = % agents with BLEI < 30 days (Crisis+Precarious)",
"positioning": "calibrated institutional simulation architecture — not econometric forecast",
"interactive_simulation": "https://bettertobest.github.io/compassionism-simulation/",
"bu_accounting_convention": {
"BLEI": "food BU effective value only: 360 BU × $2.75 = $990/adult/month",
"EPPM": "full BU blended value: 360×$2.75 + 840×$1.80 = $2,502/adult/month",
"FBS": "BU face value: $1,200 (cash-equivalent food expenditure reduction)"
},
"v31_accuracy_improvements": [
"Annual expense system: SIM_COST_SCALE depleted each year",
"PTF modeled as cost-reduction factor, not wealth injection",
"PTH: AE growth and rent savings in separate accounting categories",
"BLEI mInc: actual agent wage, not wealth/60 proxy",
"Heterogeneous automationRisk per agent [0.2, 1.0]",
"Negative wealth floor -10000 (was 0/100)",
"Population-level multi-year recessions with incomeMultiplier",
"PTF dynamic adoption via distress and diffusion",
"Tier naming: Flourishing (CCO+PTF) vs Comfortable (without)"
],
"v33_accuracy_improvements": [
"Gini zero-wealth edge case: totW=0 → Gini=0 (was ||1 fake fix)",
"NaN/Inf guards throughout agentBLEI, agentEDC, runYear, calcMetrics",
"Proper percentile interpolation: p10/p90 use linear interpolation",
"PRESET crash fix: applyPreset() guards against undefined presets",
"Wage growth diminishing returns on stability premium (M&S 2013 + Carroll 1997)",
"Recession beta(5,2) distribution over [0.70, 0.95] (NBER calibration)",
"SIM_COST_SCALE renamed from ANNUAL_BASE_COST",
"50× Monte Carlo with 95% CI (n≥10: mean ± 1.96·SD/√n)",
"Participant vs. non-participant KPI stratification tracked annually",
"Computed OAT sensitivity: 11 mini-simulations ±20% on 5 params (seed 7777)",
"Validation suite: 4 internal consistency checks",
"JSON export: full parameter + time-series snapshot",
"New High Automation preset",
"ODD protocol in assumptions panel (Grimm et al. 2010)",
"try/catch error handling in runSim() with user-visible message"
]
},
"unit_system_note": {
"wealth": "USD nominal; lognormal(10.5, 1.2) → median ~$36K; Fed SCF 2022 calibration",
"wage": "Simulation Income Units (SIU); lognormal(3.5, 0.5) → median ~33 SIU; NOT USD",
"sim_cost_scale": "Calibrated simulation scale; operative quantity is wage*12/SIM_COST_SCALE",
"interpretation": "ABM calibrated unit system — standard practice (Epstein & Axtell 1996)"
},
"v31_additions": {
"annual_base_cost": { "value": 1500, "note": "Simulation scale; v3.3 renamed to SIM_COST_SCALE" },
"wealth_floor": { "value": -10000, "note": "Allows debt/insolvency dynamics" },
"automation_risk": {
"distribution": "Uniform(0.2, 1.0) per agent",
"note": "Heterogeneous AI displacement; wage_change -= pop_rate × agent_risk"
},
"recession_model": {
"field_name": "incomeMultiplier (renamed from severity)",
"range_v31": [0.65, 0.85],
"note": "v3.1 range superseded by v3.3 beta(5,2) distribution — see v33_additions"
},
"inflation": {
"default": 0.0,
"baseline_preset": 0.03,
"ptf_damping": "effective_rate *= (1 - ptfShare * 0.5)",
"pth_damping": "effective_rate *= 0.90"
},
"blei_poverty": {
"definition": "% agents with BLEI < 30 days (Crisis + Precarious tiers)",
"target": "< 5% under full integration"
},
"ptf_mechanism": "Cost-reduction factor (0.12-0.16 of annual costs); NOT wealth addition",
"pth_mechanism": "Housing cost reduction (35%) + separate AE growth; no double-count",
"tier_naming": {
"with_cco_ptf": "Flourishing (generative system maturation active)",
"without_cco_ptf": "Comfortable (temporal stability, not full Compassionism)"
}
},
"v33_additions": {
"wage_diminishing_returns": {
"formula": "WAGE_BLEI_BONUS × 1/(1 + 0.5 × max(0, wage/WAGE_MEDIAN_SIU − 1))",
"wage_median_siu": 35,
"rationale": "Prevents structural compounding at high wages; grounded in Mullainathan & Shafir (2013) + Carroll (1997)",
"note": "BLEI is purely measurement; premium reflects underlying stability it measures"
},
"recession_distribution": {
"type": "beta(5, 2) over [0.70, 0.95]",
"mode": 0.90,
"mean": 0.88,
"range": [0.70, 0.95],
"beta_a": 5,
"beta_b": 2,
"rationale": "NBER post-WWII recession calibration; replaces v3.1 Uniform(0.65, 0.85) which overstated severe recession frequency"
},
"sim_cost_scale": {
"value": 1500,
"renamed_from": "ANNUAL_BASE_COST",
"note": "Rename avoids apparent contradiction with BASE_DAILY_COST ($68.33/day ≈ $24,941/yr vs 1,500 SIU sim scale)"
},
"gini_zero_wealth": "totW=0 → Gini=0 (mathematically correct: perfect equality of deprivation); BLEI poverty is the correct poverty indicator at zero wealth",
"nan_inf_guards": "Added throughout agentBLEI, agentEDC, runYear, calcMetrics",
"percentile_interpolation": "p10/p90 use linear interpolation; replaces index rounding",
"preset_crash_fix": "applyPreset() guards against undefined presets with console.error",
"oat_sensitivity": "11 computed mini-runs ±20% on 5 parameters, seed 7777; first-order only",
"validation_suite": {
"checks": 4,
"list": [
"BU monotonicity: BU=1200 outperforms BU=800",
"Participation threshold: 78% outperforms 45%",
"CCO benefit: full integration outperforms baseline",
"PTF dampens inflation: PTF+inflation < no-PTF+inflation"
]
},
"participant_stratification": "CCO participants vs non-participants tracked annually; tests CCO benefit without non-participant harm",
"monte_carlo_ci": "n≥10 shows 95% CI = mean ± 1.96·SD/√n; UI suppressed for n≥10 (performance)",
"json_export": "Full parameter + time-series snapshot for replication",
"presets_added": ["hiAI"],
"odd_protocol": "Grimm et al. (2010) ABM documentation added to assumptions panel"
},
"core_cco_parameters": {
"bu_monthly_per_adult": { "value": 1200, "unit": "USD", "range": [800, 1500],
"note": "FLAT allocation — not octave-scaled" },
"bu_food_eff_blei": { "value": 990, "note": "BLEI numerator (food only)" },
"bu_blended_eppm": { "value": 2502, "note": "EPPM full value" },
"bu_epsilon_food": { "value": 2.64, "range": [1.50, 3.00],
"analogue": "Mondragon Eroski: 35–55% overhead reduction" },
"bu_epsilon_utilities": { "value": 1.80, "range": [1.40, 2.50] },
"gamma_entry": { "value": 0.12, "note": "Month 0-5" },
"gamma_established": { "value": 0.20, "note": "Month 6+ (CCO floor established)" },
"participation_rate": { "value": 0.78, "range": [0.60, 0.95], "minimum_viable": 0.55 },
"phi_enhancement": { "value": 1.618, "symbol": "Φ", "elasticity": 0.12,
"note": "v3.4: Phi On: effective max ≈ quality ceiling × 1.618 (e.g., ceiling 9× → ≈ 14.56×). Phi Off: max = quality ceiling only." },
"conversion_tax": { "value": 0.12, "range": [0.10, 0.15] }
},
"pth_parameters": {
"uptake_rate": { "value": 0.20, "range": [0.15, 0.25] },
"monthly_payment": { "value": 600, "edc": 0.00, "note": "100% to Acre Equity" },
"acre_appreciation": { "value": 0.04, "range": [0.02, 0.07], "unit": "annual rate" },
"v31_note": "AE growth tracked separately from housing cost reduction — no double-count",
"ae_liquidity_haircut": {
"month_0_6": { "value": 0.15 }, "month_7_24": { "value": 0.40 },
"month_25_60": { "value": 0.65 }, "month_60_plus":{ "value": 0.85 }
}
},
"ptf_parameters": {
"market_share": { "value": 0.18, "range": [0.12, 0.25],
"calibration_status": "under revision for v3.x — earlier 30% from less rigorous sim" },
"mechanism": "Cost-reduction factor applied to annual living costs (v3.0 correction)",
"savings_rate": { "value": 0.12, "szh_amplified": 0.12 },
"overhead_reduction": { "value": 0.50, "range": [0.40, 0.60], "note": "Mondragon-analogy internal-overhead figure, NOT the simulation's own consumer-facing cost-reduction parameter — see savings_rate below (v4.7 clarification, prompted by an external audit citing this field as if it were the engine's cost-reduction rate)" },
"dynamic_adoption": {
"base_rate": 0.005,
"distress_bonus": 0.015,
"note": "v3.1: agents join via economic distress or diffusion, not only at init"
}
},
"synergy_parameters": {
"theta_min": { "value": 0.05, "at_density": 0.55 },
"theta_max": { "value": 0.25, "at_density": 0.90 },
"network_threshold": { "value": 0.55 }
},
"blei_parameters": {
"tier_thresholds_days": { "crisis":7, "precarious":30, "threshold":120,
"stable":365, "secure":730, "top_tier":"730+" },
"blei_poverty_definition":"% agents with BLEI < 30 days (Crisis + Precarious); primary poverty indicator v3.3",
"daily_cost_cco": { "value": 31.67, "unit": "USD/day" },
"daily_cost_baseline": { "value": 68.33, "unit": "USD/day" },
"edc_floor_cco": { "value": 0.025 },
"edc_near_poverty_baseline": { "value": 0.625 },
"gamma_note": "0.12 at entry; 0.20 by Month 6 (CCO floor established)"
},
"calibration_benchmarks": {
"note": "Internal calibration checks — confirm design targets, not external empirical validation",
"blei_threshold_rate_min": 0.95,
"blei_secure_rate_min": 0.80,
"gini_edc_max": 0.22,
"eppm_median_min": 2.00,
"system_stability_min": 0.90,
"blei_poverty_max": 0.05,
"participant_poverty_direction": "participant_poverty_rate <= non_participant_poverty_rate",
"validation_suite_checks": 4
},
"citation": {
"paper": "CCO-PTF-PTH-CIP-SZH Simulation Replication Framework (Johnson & Claude, 2026)",
"blei_paper": "The Basic Living Economic Index: A Temporal Stability Framework (Johnson & Claude, 2026)",
"interactive_simulation": "Compassionism Framework Simulation v4.22 — bettertobest.github.io/compassionism-simulation/",
"interactive_simulation_doi": "https://doi.org/10.17605/OSF.IO/QWTE2",
"authors": "Johnson, D. & Claude (Anthropic)",
"year": 2026,
"url": "https://bettertobest.github.io/research-hub/cco-ptf-simulation-replication.html",
"license": "CC BY 4.0"
}
}Appendix B: Related Research#
🖥️ Compassionism Framework Simulation v5.6.0 — Interactive Tool
Browser-based HTML/JavaScript agent-based simulation implementing all five architectures with BLEI-calibrated welfare metrics. v3.3 builds on the v3.1 accuracy improvements (annual expense system, PTF as cost-reduction factor, PTH without double-counting, heterogeneous AI automation risk, multi-year population-level recessions, negative wealth floor, PTF dynamic adoption, BLEI poverty KPI) and adds: wage growth diminishing returns, beta(5,2) recession distribution calibrated to NBER data, parameterized 50× Monte Carlo with 95% CI, CCO participant vs. non-participant stratification, computed OAT sensitivity (11 mini-runs), a four-check validation suite, JSON export, High Automation preset, and ODD protocol documentation. v3.5 adds a permanent archival DOI (10.17605/OSF.IO/QWTE2), fixes version-string drift between the displayed version and exported CSV/JSON files, and adds a live effective-max-multiplier indicator. v3.6, prompted by an external technical audit, relabels the PTF slider to reflect its initial-condition semantics (final share can exceed it via organic adoption), discloses the System Stability metric's current trend-heuristic formula via an info tooltip and Known Limitations entry, fixes a mobile table overflow bug, adds a cancel control for long-running Monte Carlo batches, and declares a previously-implicit global variable — no simulation math changes. v3.7, prompted by an external Meta AI review cross-checked against the actual codebase, fixes a double preset-apply bug on page load, adds screen-reader labels to all 11 charts, and adds an in-app glossary panel mirroring this page's Key Concepts accordion — no simulation math changes. v3.8, prompted by an external Grok review, isolates the baseline and CCO-only comparison-run RNG streams from the main trajectory (previously they shared one stream, so comparison-run length could shift the main run's random sequence at a fixed seed) — no simulation output changes for a fixed (years, nAgents) configuration. v3.9, prompted by an external Gemini review, tightens the p90/p10 wealth-ratio KPI's near-zero guard and fixes a case where OAT sensitivity mutated a shared params object instead of copying it; a proposed Object.freeze() on that object and a supplied Chart.js SRI hash were investigated and not applied — the freeze would have broken OAT's legitimate override, and the supplied hash is the wrong length for a valid sha512 digest. v4.0, prompted by an external Refine.ink review, is the first release where behavior changes for typical configurations: FBS-gated octave advancement, octave-governed conversion capacity, PTH payment→equity contributions, network-density-gated SZH synergy, Bass-diffusion PTF adoption, dimensionally-consistent BLEI/FBS/EDC-adjusted-Gini calculations, independently-initialised and shock-paired baseline/CCO-only comparisons, and fixes to the previously-nonfunctional Monte Carlo controls, the ablation attribution methodology, OAT's perturbation scale, and two validation-suite parameter-mapping bugs — see the v4.0 note above for the full list, including what was deliberately left unfixed and why. v4.1, an internal Claude (Anthropic) audit, fixes a paired-population ablation leak that let the SZH+PTF synergy bonus survive PTF removal on the Attribution chart when SZH was active, wires up a dormant "illustrative reference run" UI label that had never actually displayed since v3.4, and corrects several UI hint strings that had drifted from their underlying formulas (recession duration/severity, octave conversion-capacity description, an effective-max-multiplier default, a baseline-chart caption, an OAT sensitivity range) — no simulation mechanics changed, confirmed via an independent N=1,000-seed large-N re-run rather than assumed; see the v4.1 note above for the full list and the near-poverty cohort's new 40-year extension study. This paragraph itself was last updated at v4.1 and, found stale while fixing an unrelated version-number reference in v4.8, is not being backfilled through v4.2-v4.8 here — that would duplicate the Version History toggle (further up this page) rather than add anything; see that toggle and CONTRIBUTING.md for the complete, current account of every release since v4.1, including v4.8's UI fixes, relicensing to Apache License 2.0 for the source code, and validated WEALTH_FLOOR sensitivity sweep. No installation required — runs directly in any modern browser.
The Basic Living Economic Index (BLEI) — Foundation Paper
Full mathematical formalization of BLEI, EDC, EPPM, FBS, CSI, and IPBI including BU purchasing power accounting convention, Appendix A (symbol table), Appendix B (worked numerical example), ε cost pass-through model (§3.3), expanded literature review, and updated references.
Risk Mitigation Framework for CCO-PTH-CIP-SZH Implementation
Comprehensive risk identification and mitigation for the failure modes identified in the BLEI paper: governance capture, participation collapse, ε degradation from network density failure, and Acre Equity liquidity risk.
Optimal Transfer Design in Post-Scarcity Economies
Mathematical proof that CCO-PTF allocation Pareto dominates traditional welfare systems under the BLEI welfare criterion.
Dual Currency Systems and Inflation: CCO's Price Stability Mechanisms
GE analysis of BU-stimulated demand effects on non-PTF market price levels. Addresses the inflationary pressure failure mode and confirms 12% conversion tax sufficiency. PTF and PTH deflationary effects relevant to v3.1 inflation mechanism.
U.S. Real Estate Market Transformation Through PTH Integration
Validates Acre Equity parameter calibration and 20% PTH uptake modeling against US real estate market structure. Primary reference for r_a range selection and CLT analogue grounding.
Executive Summary & Research Index
Complete architectural overview of the Compassionism framework: formal definitions of CCO, PTF, PTH, SZH, and CIP, their interdependencies, and the full 18+ paper research corpus.