Skip to content

Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42

Open

Description

@MaxGhenis

Research memo for the candidate that follows the failed baseline (#40). Gate 1 stays locked; candidate 2 runs through the same protocol on the same paired splits, and its model description should be registered in this issue before the run.

1. What the locked references demand — a three-moment back-out

The committed battery autocorrelations (log earnings among positives, biennial lags 1/2/5 = 2/4/10 years) are 0.7301 / 0.6572 / 0.5389. Fitting candidate structures to exactly these three numbers:

structure implied 10yr autocorr vs locked band [0.469, 0.609]
AR(1) chain (the baseline's linear analogue) 0.207 far outside
AR(2) — "condition on two lags" (Yule–Walker fit to lags 1–2) 0.396 still outside
permanent + AR(1) transitory + white noise 0.539 exact by construction

The three-component model fits all three committed moments exactly with permanent share 0.467, transitory share 0.364 (biennial ρ = 0.722 ≈ 0.85 annualized), noise share 0.169. The long-run autocorrelation asymptote equals the permanent share — no finite-lag chain, however nonlinear, can hold a ~0.47 floor forever; the baseline run measured exactly this (1-lag QRF: 10yr autocorr 0.31–0.37 — nonlinearity buys ~+0.13 over the linear 0.207, structurally short). The noise share of 0.169 is consistent with the PSID validation literature on reporting error (Pischke 1995; Bound & Krueger's mean-reverting error), which also cautions against pushing the chain to reproduce lag-0 variance too literally.

Conclusion: candidate 2 must carry an explicit latent person-level state. Two extra lags provably cannot do it; a permanent component is not one option among several, it is what the committed moments identify.

2. Literature mapping (all verified)

  • Arellano, Blundell & Bonhomme (2017, Econometrica 85:693–734) — the theoretical home for our machinery: log earnings = a general Markovian persistent component plus a transitory innovation, estimated with quantile-based methods; persistence varies with the size and sign of the current shock (nonlinear persistence, conditional skewness). A chained QRF conditioned on a latent persistent state is the simulation-native analogue of this framework.
  • Guvenen, Karahan, Ozkan & Song (2021, Econometrica 89:2303–2339) — administrative data on millions of workers: earnings changes are strongly non-Gaussian (negative skewness, kurtosis far above normal, varying by age and earnings level), and persistence is asymmetric (positive changes for high earners are transitory; negative ones persistent; reversed for low earners). QRF quantile draws capture these facts natively where parametric AR components cannot — the reason to keep the QRF frame rather than switch to a parametric process.
  • Sabelhaus & Song (2010, JME 57:391–403) — permanent/transitory decomposition over the life cycle on SSA administrative earnings; already load-bearing in the paper's earnings section and the right external anchor for our fitted component shares.
  • Incumbent dynamic microsimulations already encode the permanent component. MINT projects earnings by splicing donor workers' observed histories onto targets and adds a person-specific fixed effect to regression projections — splicing preserves observed profile heterogeneity by construction (MINT I methods). DYNASIM carries an estimated individual-specific error term in its earnings equations. Gate 1 is demanding of us what the incumbents already build in.

3. Recommended design: latent-permanent conditioned chained QRF

Production-compatible with the backcast use case (one anchored observation per person), and implementable in populace-fit's existing chaining:

  1. Training (80% complement only): residualize log positive earnings on age; estimate each training person's permanent component as the empirical-Bayes-shrunk within-person mean of residuals (shrinkage handles short observed spells).
  2. Anchor stage: fit QRF_perm ~ P(perm | anchor-period covariates: anchor earnings, age at anchor). At generation, draw each holdout person's perm from this conditional — never estimate it from holdout data (protocol: holdout persons are excluded from all fitting; their perm is a model draw conditioned only on the anchor row the production use case also has).
  3. Chain stage: the existing backward one-step chain, now conditioned on the latent draw: earnings at t−2 ~ QRF(earnings at t, perm, age at t−2). Regime gate unchanged (participation conditions on perm too, which should also help zero-spell structure).

Why it passes structurally: conditioning every step on a persistent latent state puts a floor under long-horizon autocorrelation equal to the share of variance the drawn perm explains — the exact quantity the back-out says must be ≈ 0.47 — while the QRF supplies ABB-style nonlinear persistence and GKOS-style non-Gaussian shocks within each step. The honest uncertainty is that P(perm | one anchor year) is wide; that spread is real and belongs in the draws.

Alternatives considered and ranked below it:

  • Two-lag conditioning only — rejected above; the linear benchmark (0.396) sits outside the locked band before nonlinearity, and the burden it must close (~0.14) exceeds what nonlinearity bought at one lag (~0.13) starting from much further away.
  • Full ABB quantile-AR estimation — estimates the latent process with quantile regressions and simulates from it; theoretically cleanest but duplicates what QRF chaining does natively at higher implementation cost; better used later as a cross-check.
  • GKOS-style parametric mixture process — benchmark-grade moments but abandons the nonparametric frame and its automatic covariate conditioning; useful as an external comparison line in the paper, not as the candidate.
  • MINT-style splicing — preserves heterogeneity by construction and is a strong sanity baseline, but it cannot condition on arbitrary covariates or extend to the full transition system; worth reporting as a comparison row if cheap.

4. Pre-registration discipline for the run

Same locked gate, same paired splits (seeds 0–4), one shot, no tuning against holdout scores. Before the run, this issue gets a comment registering the exact candidate spec (feature list, shrinkage rule, QRF defaults); the run publishes pass or fail, as #40 did.

Activity

  1. MaxGhenis commented on Jul 5, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-2 registration (frozen before the run). Model: latent-permanent conditioned chained QRF, per this issue's memo. Every degree of freedom below is fixed now; the run is one shot on the locked gate (same protocol, paired seeds 0-4), publishing pass or fail.

    Stage 0 — residualization (train split only). For train person-periods with positive earnings: r = log(earnings) − f(age), where f is a weighted OLS quadratic in age (weights = person-period weights), fit pooled on the seed's 80% complement.

    Stage 1 — person effect. perm_i = empirical-Bayes shrunk within-person mean of residuals: perm_i = n_i·mean_i / (n_i + λ), λ = σ²_within/σ²_perm from an unweighted one-way random-effects method-of-moments decomposition on train residuals (persons with n_i ≥ 2 identify σ²_within; σ²_perm = max(0, var of person means − σ²_within · mean(1/n_i))). Persons with zero positive observations get perm_i = 0 (the prior mean, n_i = 0).

    Stage 2 — perm draw model. RegimeGatedQRF (populace-fit defaults), target perm_i, predictors = (anchor-period earnings level, anchor-period age), where a person's anchor is their chronologically last observed period in the filtered panel; training rows = train persons' anchor rows, sample_weight = anchor-row weight. At generation each holdout person's perm is a DRAW from this model conditioned on their anchor row (holdout perms are never estimated from holdout data), seeded from the gate seed.

    Stage 3 — chain. Identical to the baseline runner (backward one-step biennial chain, anchor keeps its real value, one-step across observation gaps, regime gate for zeros), with ONE change: the transition QRF's predictors are (earnings at t, age at t−2, perm), where perm = the estimated perm_i for training pairs and the drawn perm for generation. Weights as in the baseline (earlier-period person-period weight). populace-fit defaults throughout; all RNG seeded from the gate seed.

    Artifact: runs/gate1_qrf_latent_perm_v1.json, same schema as the baseline artifact, model description updated. Scored via the identical protocol runner against the locked gates.yaml thresholds.

  2. MaxGhenis commented on Jul 5, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-3 registration (frozen before the run). One amendment to candidate 2, motivated by its published diagnosis (PR #43: drawn perm share 0.52–0.55 vs the structural 0.467; 10yr autocorr overshoot as the sole failing battery stat): the white-noise variance decomposition in stage 1 over-attributed variance to the permanent component because the transitory component is itself persistent and does not average out over a person's few biennial observations. Stage 1 is replaced by a persistence-aware decomposition, estimated on each seed's train split only (never from the committed reference artifacts). All other stages, features, defaults, seeds, and the protocol are identical to the candidate-2 registration above.

    Stage 1a — within-person autocovariances (train split). From the stage-0 residuals, compute pooled unweighted within-person autocovariances γ_k for biennial lags k = 0..5: for each lag, average r_it·r_i,t+2k over all within-person pairs of observed periods exactly 2k years apart (γ_0 over all residual squares), centering residuals at the pooled mean.

    Stage 1b — three-component fit. Fit γ_0 = σ²_perm + σ²_trans + σ²_noise and γ_k = σ²_perm + σ²_trans·ρᵏ (k = 1..5) by: grid ρ ∈ {0.50, 0.51, …, 0.95}; at each ρ solve the six moment equations for (σ²_perm, σ²_trans, σ²_noise) by non-negative least squares; select the ρ minimizing the sum of squared moment residuals. Deterministic; no iterative starting-point sensitivity.

    Stage 1c — person effects with correlated-noise shrinkage. perm̂_i = w_i · mean_i(r), where w_i = σ²_perm / (σ²_perm + V_i) and V_i = (1/n_i²)·[σ²_trans·Σ_j Σ_l ρ^{|p_j − p_l|/2} + n_i·σ²_noise] over person i's observed periods p (biennial lag distances); persons with no positive observations get perm̂_i = 0 as before.

    Stages 0, 2, 3 unchanged from the candidate-2 registration (same anchor definition, same QRF_perm predictors, same chain predictors and gap/zero rules, populace-fit defaults, all RNG from the gate seed). Artifact: runs/gate1_qrf_latent_perm_v2.json, same schema, with the stage-1 fit (γ_k, chosen ρ, component variances, per-seed perm-share diagnostics) reported-not-gated. One shot on the locked gate; publishes pass or fail.

  3. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-4 diagnosis and registration (frozen before the run).

    Diagnosis from the three published runs

    Mean autocorrelation ladders (2yr / 4yr / 10yr; locked bands 0.730±0.05 / 0.657±0.06 / 0.539±0.07):

    run 2yr 4yr 10yr drawn perm share
    baseline (#40) 0.726 0.573 0.333 —
    candidate 2 (#43) 0.726 0.688 0.649 0.52–0.55
    candidate 3 (#44) 0.722 0.695 0.647 0.21–0.27

    Candidates 2 and 3 have statistically identical ladders despite a 2.3× difference in drawn-perm variance, and their shared 10-year floor (~0.648) equals the raw train person-mean variance share (candidate 2's own MoM diagnostic: 0.647). This is errors-in-variables scaling: rescaling the person-effect feature makes the QRF rescale its response inversely, so generation transmits the same total person-level variance either way — the raw person-mean share (inflated by transitory bleed over few biennial observations), not the decomposed permanent share. The imputation sensitivity (#46) rules out target contamination (excluding assigned observations moves the reference only +0.012). Conclusion: no feature-side dial can fix this; the person-level variance and the observation-noise layer must be set structurally at generation.

    Candidate-4 registration: structural three-component generation

    Stages 0 and 1 are identical to the candidate-3 registration above (age-quadratic WLS residualization; within-person autocovariances; grid-ρ NNLS decomposition into σ̂²_perm, σ̂²_trans, ρ̂, σ̂²_noise; correlated-noise-shrunk perm̂_i) — all per seed on the train split. The generation changes:

    Participation (zero vs positive): unchanged from the prior candidates' machinery — the regime gate decides zero/positive per backward step conditioned on the next period's generated level and age, trained on the 80% complement, populace-fit defaults. (This channel passed every battery and geometry check in all three runs.)

    Magnitude (positive observations), log scale, assembled from components. For each holdout person, with their anchor (chronologically last observed period) kept at its real value:

    1. Person effect: m_i = μ̂(anchor_i) + η_i, where μ̂ is the conditional median from QRF_perm (target perm̂_i, predictors = anchor earnings level and anchor age, trained on train anchors — as registered for candidate 2), and η_i ~ N(0, max(0, σ̂²_perm − Var_train[μ̂(anchor)])) with Var_train computed over train persons' anchors. Total cross-person variance of m thus equals σ̂²_perm by construction; the anchor informs its location only.
    2. Transitory chain: t̂_it = r_it − perm̂_i on the train split defines training pairs; QRF_t (populace-fit defaults) fits t_prev | (t_next, age_prev). At generation, t_anchor,i = r_anchor,i − m_i (the anchor's observed residual less the drawn person effect; this start absorbs the anchor's observation noise — accepted and documented), then chain backward over the person's observed periods with the one-step kernel, one step across gaps as before.
    3. Observation noise: ε_it ~ N(0, σ̂²_noise) drawn i.i.d. per generated positive observation (none at the anchor, which keeps its real value).
    4. Assembly: generated positive earnings = exp(f̂(age_it) + m_i + t_it + ε_it), where f̂ is the stage-0 fit.

    All RNG seeded from the gate seed; populace-fit defaults throughout; thresholds read from gates.yaml at runtime; same paired splits; battery-reference bit-exact precheck; one shot, publishes pass or fail. Artifact: runs/gate1_qrf_structural_v1.json (same schema; model description per this comment; reported-not-gated diagnostics: per-seed σ̂ components, Var_train[μ̂(anchor)], realized cross-person var(m), realized generated-vs-train variance ratio).

    Predicted mechanics, stated for the record: the 10-year floor should fall from ~0.648 toward the decomposed permanent share, the noise layer should attenuate all lags toward the data's measured ladder, and the main residual risks are the 2-year band (if the assembled short-lag correlation undershoots) and the upper tail blocks under lognormal noise. Whatever the numbers say, they publish.

  4. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-5a registration (frozen before the run): MINT-style donor splicing. Motivated by the four-run symmetry (conditional-draw candidates cannot set person-level variance; the structural candidate broke the marginal): splicing reuses whole observed careers, inheriting marginal AND person-level dependence from real data at once — the strategy SSA's MINT uses (donor earnings records spliced onto targets). This candidate doubles as the field-standard benchmark: if it passes, the gate has its first PASS and the generative program has the bar it must beat on conditioning flexibility; its production limits (donor support, coarse covariate conditioning) are why the generative track continues.

    Per seed (same locked protocol, paired splits, seeds 0-4):

    1. Donor pool = the seed's 80% train persons' observed trajectories in the locked filtered panel.
    2. Match rule (deterministic): for each holdout person, candidate donors are train persons observed in the same calendar PERIOD as the holdout's anchor (chronologically last observed period) with age within ±2 years of the holdout's anchor age; among them select the donor with the smallest absolute difference in anchor-period earnings; ties broken by smaller absolute age difference, then smaller person_id. If the cell is empty, widen the age window in ±2 steps (±4, ±6, …) until non-empty (widening count reported in the artifact).
    3. Splice (age-indexed, whole-career, one donor per holdout person): for each holdout observed period t other than the anchor, the generated value is the donor's earnings at the holdout's age at t; if the donor is not observed at that exact age, use the donor's earnings at the nearest observed age (ties → younger); fallback usage rate reported.
    4. Level adjustment: if both the holdout's anchor earnings and the donor's earnings at (nearest to) the anchor age are positive, all spliced values are multiplied by their ratio, clipped to [0.2, 5]; otherwise no scaling (donor values copy raw, zeros stay zeros).
    5. Anchor keeps its real value, as in every prior candidate. No RNG is needed (fully deterministic given the seed's split); the gate seed enters only through the split.

    Artifact: runs/gate1_splice_v1.json — same schema; spec_registration = this comment URL; reported-not-gated diagnostics: donor-match age-window widening distribution, nearest-age fallback rate, scaling-clip rate, donor reuse counts (how many holdout persons share a donor). One shot on the locked gate; publishes pass or fail. The parallel design track (candidate 5b, to be registered separately after design work): rank-space persistent dynamics with empirical quantile marginals and simulated-moment-calibrated innovations, per the ABB/GKOS synthesis in the research memo.

  5. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-5a′ registration (frozen before the run): segment splicing. The single-donor run (PR #50) failed on three named artifacts; this variant — closer to MINT's actual practice — addresses each by construction: segments from multiple donors mix careers (against whole-career over-persistence), splices are indexed by calendar period rather than age (against the offset-age-grid signature), and level adjustment happens per segment at its boundary (against global anchor-noise conversion). Everything below is deterministic; the gate seed enters only through the locked split.

    Per seed (locked protocol, paired splits, seeds 0-4), for each holdout person, walking BACKWARD from the anchor (chronologically last observed period, kept at its real value):

    1. Segmentation. The person's observed periods before the anchor are grouped, in backward order, into consecutive segments of up to 3 observed periods each (the last segment may be shorter).
    2. Boundary. Each segment's boundary is the person's next-later observed period — already filled by a previous segment, or the anchor itself for the first segment. The boundary value b is the target's value there (real at the anchor, spliced otherwise).
    3. Donor match (deterministic). Candidate donors for a segment are the seed's train persons observed at ALL of the segment's calendar periods AND at the boundary's calendar period, with age at the boundary period within ±2 years of the target's age there. Among candidates, select the donor minimizing |donor earnings at the boundary period − b|; ties broken by smaller absolute boundary-age difference, then smaller person_id.
    4. Fallback cascade. If no candidate exists, widen the age window in ±2-year steps (±4, ±6, …) up to ±10; if still empty, shorten the segment by one period (re-grouping the remainder into the following segment) and retry from the original window; a one-period segment with a same-period boundary requirement always has candidates in practice (rates reported).
    5. Splice and scale. The segment's generated values are the donor's earnings at the segment's own calendar periods, multiplied by b / (donor's earnings at the boundary period) clipped to [0.2, 5] when both are positive; if either is zero or negative the segment copies unscaled. Donor zeros copy as zeros.

    Artifact: runs/gate1_splice_v2.json — same schema; spec_registration = this comment URL; reported-not-gated diagnostics: segment-count and segment-length distributions, age-window widening and segment-shortening rates, scaling-clip rate, donor reuse (distinct donors per person and per holdout), boundary-value match error distribution. One shot on the locked gate; publishes pass or fail. No model fitting; runs in the plain repo venv.

  6. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-5b registration (frozen before the run): rank-space generative dynamics. The design composes the field's third and fourth strategies (paper §gate-1): empirical quantile marginals (rank space — the marginal cannot break by construction, run 4's lesson), a fixed-form latent persistence model no learner can rescale away (runs 2–3's lesson), and simulated-moment calibration so iterated — not one-step — dynamics match the ladder (the baseline's lesson). Participation reuses the regime machinery that has passed in every run. Target: beat the classifier residual segment splicing left (pairs c2st 0.547, runs 0.567) while keeping its battery. Known limitations, stated up front: Gaussian-copula innovations cannot represent the asymmetric, income-level-dependent persistence documented in administrative data, and cell-discretized quantile support may itself be classifier-visible.

    All stages per seed on the locked split; train = the 80% complement throughout; holdout data appears only at scoring and as each person's real anchor.

    1. Marginal machinery (train only). Cells = five-year age bins {25–29, …, 55–59} × calendar period. Per cell: p0 = weighted zero share; for positive earnings, the weighted empirical quantile function Q̂⁺ — sort values y_k with weights w_k, set w̃_k = (cum_k − 0.5 w_k)/W, and interpolate (w̃_k, y_k) linearly, clamping to [y_min, y_max]. The rank function r̂ is the inverse interpolation (y_k → w̃_k), clamped to [0.001, 0.999].
    2. Latent model. z_it = a·w_i + b·x_it + c·ε_it with w_i ~ N(0,1) person-permanent, x_it stationary AR(1) in N(0,1) with coefficient ρ, ε_it i.i.d. N(0,1), a,b,c ≥ 0, a²+b²+c² = 1; U_it = Φ(z_it).
    3. SMM calibration (train only). Because participation is independent of the magnitude latent, the among-positives autocorrelation of the deployed process equals that of the magnitude process, so calibration simulates the magnitude process alone: subsample = the first 5,000 train persons with positive anchors ordered by person_id, each on their actual positive-period support, anchored at their real anchor exactly as generation does (step 4), internal RNG seeded from the gate seed. Targets: the train panel's battery autocorrelation at lags 1/2/5 (locked definitions, among positives). Grid: a² ∈ {0.25, 0.30, …, 0.60}, ρ ∈ {0.55, 0.60, …, 0.95}, c² ∈ {0.05, 0.10, …, 0.35}, requiring b² = 1 − a² − c² ≥ 0.05; one simulation per point; equal-weight SSE over the three targets; ties broken by smaller a², then smaller ρ, then smaller c².
    4. Generation (holdout). Anchor keeps its real value. For a positive anchor, u_A = r̂ at the anchor's cell of the anchor value; for a zero anchor, u_A = p0/2 of the cell; z_A = Φ⁻¹(u_A). Initialize the state by the exact Gaussian conditionals: w | z_A ~ N(a z_A, 1 − a²), then x_A | z_A, w ~ N(b(z_A − a w)/(b² + c²), c²/(b² + c²)). Chain backward over the person's observed periods: x_prev = ρ·x_next + √(1−ρ²)·ν with ν ~ N(0,1), one step per observed-period transition regardless of gap width (the standing gap rule). Per generated period: z = a w + b x + c ε with fresh ε; U = Φ(z). Participation comes from the regime machinery exactly as in the candidate-2 registration (backward gate on next generated level and age, trained on the 80% complement, populace-fit defaults); where positive, earnings = Q̂⁺ of the period's cell at U. All RNG streams seeded from the gate seed with distinct fixed substream labels (w, x-innovations, ε, gate).
    5. Artifact: runs/gate1_rank_v1.json — standard schema; spec_registration = this comment URL; reported-not-gated diagnostics: chosen (a², b², c², ρ) per seed with the SMM target-vs-simulated moments and minimum SSE, anchor-rank distribution, cell-count distribution, share of clamped ranks.

    One shot on the locked gate; publishes pass or fail.

  7. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-6 registration (frozen before the run): anchored empirical rank-transition kernel. Run 7 validated the rank-space architecture and rejected Gaussian innovations (mobility churn; classifier-readable joint). This candidate keeps the architecture — train-cell empirical quantile marginals, the proven regime gate, real anchors — and replaces the latent-Gaussian dynamics with a nonparametric transition law estimated by counting, which (a) captures nonlinear, extreme-sticky rank persistence empirically (what the mobility diagonal demands), (b) draws empirical innovations (what the classifier residual demands), and (c) carries the person-level component through anchor-rank conditioning rather than any learner-visible feature — a counting estimator has no rescaling freedom, so the errors-in-variables channel of runs 2–3 is closed by construction. No calibration stage: the kernel has zero free parameters beyond the pinned discretization; iterated dynamics are whatever the counts imply, and the run publishes them.

    All stages per seed on the locked split; train = the 80% complement; holdout appears only at scoring and as each person's real anchor.

    1. Rank machinery — identical to the candidate-5b registration (five-year age bins × period cells; weighted empirical quantile function Q̂⁺ and rank function r̂ with the same interpolation and [0.001, 0.999] clamps; zero share p0 per cell).
    2. Anchor bins. Each person's anchor rank u_A: for positive anchors, r̂ at the anchor's cell; for zero anchors, p0/2 of the cell. Anchor bin A = which fifth of (0,1) u_A falls in (five equal bins).
    3. Kernel estimation (train, counting only). For every train backward-adjacent pair among positives (both periods positive, consecutive observed periods), compute (u_prev, u_next) via r̂ in each period's own cell, and the person's anchor bin A. Accumulate weighted counts into a 5 × 20 × 20 array N[A, bin(u_next), bin(u_prev)] over twenty equal rank bins, using the pair's earlier-period person weight. Add-one (Laplace) smoothing per row; row-normalize to a transition kernel P[A, j, ·].
    4. Re-entry distribution (train, counting). Per anchor bin, the weighted distribution over twenty rank bins of u_prev for train pairs where the LATER period is zero and the earlier is positive (rank at re-entry into work, walking backward); add-one smoothed and normalized. If a person's chain crosses a zero spell, the next positive period's rank draws from this distribution.
    5. Generation (holdout, backward). Anchor keeps its real value; its rank u_A and bin A as in stage 2. Participation per step from the regime machinery exactly as in the candidate-2 registration (backward gate on next generated level and age, populace-fit defaults). For a positive step whose next period is positive: draw the previous rank bin j from P[A, bin(u_next), ·], then u_prev uniform within bin j. For a positive step whose next period is zero: draw from the anchor bin's re-entry distribution, then uniform within bin. Earnings = Q̂⁺ of the period's cell at u_prev. One kernel step per observed-period transition regardless of gap width (the standing gap rule). All RNG substreams seeded from the gate seed with fixed labels (gate, kernel-bin, within-bin, re-entry).
    6. Artifact: runs/gate1_rank_kernel_v1.json — standard schema; spec_registration = this comment URL; reported-not-gated diagnostics: kernel diagonal mass by anchor bin, kernel corner (top-bin → top-bin and bottom → bottom) mass, pair counts per anchor bin, re-entry distributions, anchor-rank distribution, clamped-rank share.

    Stated risks, for the record: the anchor-bin between-person share sets the long-run autocorrelation floor and may undershoot the 10-year band (the kernel is one-step Markov within anchor bin); the twenty-bin discretization and within-bin uniform may themselves be classifier-visible. One shot on the locked gate; publishes pass or fail.

  8. MaxGhenis commented on Jul 6, 2026

    @MaxGhenis
    ContributorAuthor

    Candidate-7 registration (frozen before the run): k-nearest-neighbor conditional rank bootstrap with anchored two-step memory. The forensics (PR #54) localized candidate 6's entire classifier residual to joint-transition flattening from discretization (20 bins + within-bin uniform + Laplace), compounding along the chain, while its marginals were exact; the battery residual (4/10-year autocorrelation) is insufficient memory. This candidate replaces the discretized kernel with continuous empirical conditional draws and deepens the conditioning memory. It stays a counting-flavored estimator — no learner (the errors-in-variables channel stays closed), no calibration stage, no scaling of values (the splice-tail defect has no analogue here). All constants below are design choices fixed a priori at registration, not tuned.

    Stages 1–2 (rank machinery, anchor rank u_A) and participation (regime gate) are identical to the candidate-6 registration. The transition law changes:

    1. Donor pools (train, per seed). From train persons' positive observations, build backward-adjacent records: pairs (u_prev, u_next) as in candidate 6, and where the person is also positive at the next-later observed period after u_next, triples (u_prev, u_next, u_next2). Every record carries the person's continuous anchor rank u_A and the pair's earlier-period person weight.
    2. Conditional draw (continuous, k-NN). For a backward step at which the target's next two generated-or-real ranks are (v1, v2) and anchor rank is a: candidate records are triples when v2 exists, else pairs; distance d = |u_next − v1| + 0.5·|u_next2 − v2| (triples only) + 0.25·|u_A − a|. Take the k = 25 nearest records (ties by record order after a pinned stable sort on person_id, period); draw ONE record with probability proportional to its weight (seeded substream); the generated u_prev is that record's u_prev exactly — a continuous empirical innovation; no binning, no smoothing, no within-bin jitter. Earnings = Q̂⁺ of the target period's cell at u_prev (interpolated in the target cell, so no value duplication).
    3. Zero crossings. Where the next period is zero, the re-entry pool is candidate 6's re-entry pairs with the same k-NN rule on d = |u_A − a| alone.
    4. Artifact: runs/gate1_rank_knn_v1.json — standard schema; spec_registration = this comment URL; reported-not-gated diagnostics: neighbor-distance distributions, triple-vs-pair usage share, donor-record reuse, effective corner mass of drawn transitions by anchor quintile (comparability with candidate 6), clamped-rank share.

    Registered risks: the 10-year autocorrelation floor may still undershoot (two-step-plus-anchor memory may not carry the full permanent share); k = 25 fixed a priori may under- or over-smooth the conditional; k-NN draws reuse train u_prev values whose fine-grained distribution the classifier may read. One shot on the locked gate; publishes pass or fail.

  9. 237 remaining items

  10. MaxGhenis commented on Oct 15, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2: registration of the 2010→2014 transport gate

    Status: registration, posted on issue #42 before B2's floor builder runs. Nothing below states a B2 outcome; none exists.

    Verification class: retrospective regression (design §3.2). Admitted scope: "2010→2014 continuity on the stated surface, ages 25–64. Not a 2010-vintage forecast" (design line 150).

    Headline (d781). B2's gate covers earnings levels and zero rates only: p50, p90 and the zero rate of annual earnings, by cohort. It does not cover changes, dispersion, mobility or persistence. H and B3L rely on B2 for levels only. The other ten cells are scored and published report-only. The result's headline names the cells the ladder keeps gated, since pruning may leave fewer than six.

    2012 and 2014 informed earlier selection. Every evaluation row of B2 lies in years the B0.1 audit lists as exposed (audit §5). They were selection evidence for candidate 3's law, and boundary-2008 and boundary-2010 candidate results were published (PRs #255 and #273). EXPOSURE_UNRESOLVED therefore fires, and this registration is the registered fallback at the §3.2 scope above (d693 Q8 (b)). H and B3L may rely on B2 only within that scope. Removing this statement after any candidate run requires a dated amendment.

    The frozen record

    B2 registers on the B0.1 addendum and audit, as merged to master by PR #505 at 3d23760d:

    File SHA-256 at 3d23760d
    docs/design/track_b_b0_1_addendum.md f719edd9bdfc969c014f2bf2d7b854d2913da9b4225d64a84a78fa53c909cd12
    docs/design/track_b_b0_1_audit.md 16b029728cfd09ef1c879bb9419cba2ec2c5dc92d6f0355498897c11c8fbb128

    The addendum's section 3 lists all ten §5.2 elements and the named power procedure as frozen. Elements 6 and 7 are frozen as rules, and B2's floor produces their values before any candidate run. Its blinded review (d693 Q6) was approved in rounds 7, 8, 9 and 10 (section 15).

    Rulings carried.

    • d693: the audit's eight defaults, with riders.
    • d622: a B1 v2 pass may unlock B2, with the weaker label carried.
    • d765: the refit arm's household half-samples.
    • d781 (a): gate the six level cells.
    • d782: Rao-Wu design-cluster standard error.
    • d1342 (A): proceed as ruled; B2's floor-build ladder decides.

    The ruling texts are quoted in the addendum's section 2.

    Inherited evidence

    B1 v2 passed as a reconstructed reproduction (weaker than bit-for-bit) (d622). It returned RECONSTRUCTED_REPRODUCTION, with zero differences on all four conditions. Its registration is #42 comment 6110674288 and its result #42 comment 6113379406. The result discloses one launcher deviation: the launcher's shell ended with the operator's session while the run went on to complete, so its exit code was not captured.

    Candidate 3's disclosure, in full (design lines 29–31):

    Candidate 3 records a valid M6 pass; it certifies nothing about mortality drift. The artifact records four passing seeds, q=0.55 and rho=−0.6. Fits use data through 2014; gated demographic flows cover 2015–2019 and earnings cover 2016/2018. The 2020–2022 window is diagnostic only. Certification is conditional on prior structural and same-holdout surface selection. The v4 point estimate p_gate=0.9018301 clears 0.90 by 0.0018301, but its clearance is not statistically resolved: the reported jackknife SE is approximately 0.013–0.019. Synthesized support, redrawn initial states and endogenous widowhood require the decision-5 successor gate.

    Elements

    1. Source release. The PSID files B2 reads are pinned by the B0.1 counts record's b2_read_set_sha256: 80 files, the .txt and .sps of every family wave 1968–2015, plus IND2023ER.txt and IND2023ER.sps. Its canonical SHA-256 (sorted keys, compact separators) is 787725e30d2273a76854e134a41b882110c76e222925ea8022ac891f413fb62c. The floor builder refuses to open any other PSID file and checks every opened file's hash against the pin.

    • Wage vintage: policyengine-us 1.752.2 nawi.yaml, realized through 2010, with later years from the fitter's log-linear projection over 2001–2010. It is labelled pe_us_1_752_2_nawi_realized_2010_loglinear_2001_2010, not ols_log_nawi_2005_2014. It is the series as later revised, not a 2011-era release.

    2. Variables and the codebook concept check (audit §4.2, definitions only). All six labor-income variables define the concept as labor income "Excluding Farm and Unincorporated Business Income". The concept reads the same across B2's window. The one difference is that market gardening is absent from the 2015 head entry's component list; whether the 2015 instrument dropped or moved it was not checked.

    3. Population. The 2011 anchor persons with a valid 2010 row: 13,542 persons, defined from data by the floor builder and checked against the frozen count. No one is scored outside ages 25–64 at the scored waves. B2's bridge hands this domain to H (d693 Q7).

    4. Exposure history. As stated above. Each gated cell carries an exposure label, and the floor artifact's headline counts them. earn_zero_rate.older carries "published candidate-3-law outcomes at boundaries 2008 and 2010". The other five gated cells carry "published five-seed floor detail, no published candidate outcome".

    5. Holdout partition.

    • Estimation: earnings rows with reference year 2010 or earlier, fitted at ages 25–64.
    • Evaluation: realized 2012 and 2014 rows on the frozen 2010 domain, at collection-wave ages 25–64.
    • Split unit: the 2011 household, for floor seeds 0–99 and gate seeds 0–4.

    6. Critical cells (d781 (a), d1342 (A)). The gate family is earn_p50.older, earn_p50.prime, earn_p90.older, earn_p90.prime, earn_zero_rate.older and earn_zero_rate.prime. The floor builder applies eligibility and M6's ladder inside the six on B2's own floor:

    • Order the eligible cells by descending tolerance/σ, ties by name. σ is M6's realized σ, √(mean² + SD²) of the 100 floor seeds' scores (m6_cells.py:681), not their SD.
    • Compute gate power by the ruled binding evaluation (scripts/track_b_b0_1_ruled_power.py), with the critical value t*_{m,ν}.
    • Stop at power 0.90 or more. Never prune a family's last cell.
    • At least four cells must remain, and not every tolerance may sit at its cap.

    Either of two cases makes B2 WEAK_POWER_OR_VACUITY at floor build, before any candidate run, and B2 stops there:

    • The ladder or a guard fails: it cannot reach 0.90 with at least four cells, or every tolerance sits at its cap. Options B–D of d1342 then return to Max, priced on B2's real tolerances.
    • ν is 0 after the stratum rule's merges, or an eligible cell's σ, se_up or τ is zero or undefined. Rescoping cannot cure these. Max is asked how the critical value, or the uninformative cell, is to be handled (addendum section 5).

    No rescoped or amended registration is written before he rules.

    7. Tolerances. tol_c = min(round(mean_c + 3 SD_c, 3), cap) over the 100 household-split floor seeds; the caps are ln 1.5 and 0.15. The floor artifact states each gated tolerance in plain units before any candidate outcome. §5.3's earnings limits do not apply to B2: no Track B gate holds ages 25–61 earnings to a fixed limit (d693 Q4).

    8. Uncertainty.

    • se_up: 4,000 household-bootstrap replicates, × √(1 + 1/K).

    • se_boot: 4,000 design replicates, Rao-Wu within strata, with the law and the paths fixed. The stratum rule (addendum section 7):

      • two-cluster strata follow arm D's rule;
      • one-cluster strata merge into the nearest stratum by code among those with two or more clusters, the lower code on a tie; all merges are applied together, before anything else; if no stratum has two or more clusters, all merge into one, which takes the lowest original code;
      • strata of three or more clusters get exact Rao-Wu at scale L;
      • ν is counted after the merges.

      The floor builder publishes the strata by cluster count, the merges, L and ν before any candidate run. A stratum of three or more clusters draws rng.multinomial(n_h − 1, 1/n_h) over its clusters sorted by original (stratum, cluster), and strata are visited in ascending merged code, all at the commit pinned below.

    • se_c = √(se_boot² + a_c se_up²). a_c is the cell's registered estimation-variance ratio: e_B2 from the planning record, plus the transported cross term f × c where protocol v2's rule adds it.

    • Bound: |g_c| + t*{m,ν} se_c ≤ tol_c, where t*{m,ν} = t_ν⁻¹(1 − 0.05/(2m)). At ν = 63 and m = 6 it would be 2.724; B2 uses its own ν.

    • Monte Carlo rule: the per-draw signed gap's SD / √K must be below tol_c/10.

    • A cell with more than 1% of its replicates undefined cannot pass.

    9. Pass conjunction. A gated cell is valid on a frame when its truth is defined, none of its K draws is undefined, and the draws are not all identical. A gate seed passes when every gated cell's side-A gap lies within tol_c. The outcome is the first that applies:

    1. INVALID: a gated cell invalid on full support or on a gate seed's side A.
    2. FAIL: fewer than four of five gate seeds pass, or some |g_c| > tol_c.
    3. PASS: every bound within tolerance and every precision rule holding.
    4. INCONCLUSIVE: otherwise.

    INVALID and INCONCLUSIVE are non-passes with FAIL's consequences.

    10. Reference dates versus collection (audit §4.3).

    Quantity Reference year Collected at
    Anchor earnings 2010 2011 interview (FAM2011ER)
    Horizon earnings 2012 2013 interview (FAM2013ER)
    Horizon earnings 2014 2015 interview (FAM2015ER)

    Age and weight are measured at the collection wave. PSID has no 2012 or 2014 interview. All staged files are retrospective releases, made 9 to 13 years after their reference years.

    Seeds and streams. The scoring root is 20261002. Household replicate b uses stream [root, b, 0], design replicate b stream [root, b, 4]. Candidate 3's law is fitted with seed 5200 and projected with draws 6200–6219 (K = 20). Floor seeds are 0–99, gate seeds 0–4.

    Planning values. They come from a 2006 pseudo-origin, whose truth years, 2008 and 2010, fall in a recession. Neither their transport to 2012 and 2014 nor r's is tested.

    Rules the implementation adds to the addendum's text

    Each was chosen in review before any run, and is stated here so nothing is decided after an outcome:

    1. Copies past 999. The planning script's copy_persons numbers copies person × 1,000 + k and refuses 1,000 or more. Below that, B2 calls it unchanged. Above it, which only a stratum rule scale L large enough can produce, B2 runs the same construction with a wider power-of-ten identifier scale. Only the identifiers' encoding differs.

    2. Eligibility is section 5's exactly: every seed defined, at least 20 events in the weaker half, and planning values present. A cell of the six that fails it is scored report-only, and the ladder runs on the others.

    3. An eligible cell with zero or undefined σ, se_up or τ has an uninformative tolerance or no computable power, and section 5 never drops an eligible cell. So B2 stops at floor build as WEAK_POWER_OR_VACUITY.

    4. Replicates that raise count as undefined for every cell, with the exception class recorded. A memory or operating-system error stops the run.

    5. An undefined truth makes the cell invalid on that frame, as in M6.

    6. The report-only interval is g ± t_ν⁻¹(0.975) se_c.

    7. More than 1% undefined household replicates in a gated cell is known at floor time, as cells[c].se_up.undefined_share_ok in the floor artifact, and that cell cannot pass.

      • The code does not apply this rule: such a floor still records outcome: LADDER_CLEARED. The operator reads undefined_share_ok for every cell in surface.gated and posts the values on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42.
      • If any is false, the scorer is not launched. By this registration B2's status is WEAK_POWER_OR_VACUITY ("undefined cells", design line 454), whatever the artifact's outcome reads. B2 returns to Max with options B–D, priced on B2's real tolerances, so B2 is not spent on a run that cannot pass.
    8. The artifact's read_set_complete field is fixed at true in the code. It means only that every opened PSID file was in the pinned read set with its pinned hash. The files actually opened are listed with their hashes in psid_files_opened_sha256; protocol v2 opened 74 of the 80.

    9. "To B2" items carried in code and tests:

      • the root-parameterised copies of anchor_multiplicities and design_multiplicities, called with root 20261002;
      • the differential test that, at root 20260930, they equal the planning functions for replicates 0-99;
      • the test that nothing in B2 reads BOOTSTRAP_ROOT_SEED;
      • the test that the scorer and arm F build identical copied frames;
      • the stratum rule's tests, including the differential with arm D at L = 1.

      They are in tests/test_track_b_b2_rules.py, tests/test_track_b_b2_design.py and tests/test_track_b_b2_pipeline.py.

    Interruption.

    • The attempt log. After every check that needs no PSID data, and before any PSID read, the scorer creates runs/track_b_b2_score.attempt.jsonl exclusively. It appends each stage there, synced to disk, and refuses to start if the file exists.
    • A refusal before the log exists reads no PSID data and is not an attempt, for example on a wrong POPULACE_DYNAMICS_PE_US_DIR. The cause is fixed and posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42, and the scorer is relaunched.
    • A floor build that ends without its artifact:
      • If it was interrupted (killed, out of memory, host failure), it may be rerun at 22e3f82b, since it is truth-only and deterministic. Its stderr and the interruption are posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42. A partly written artifact is moved outside the clone, hashed and disclosed first.
      • If it was refused by one of its checks (frozen counts, read guard, protocol hash), the refusal is published and goes to Max. A deterministic refusal would only repeat.
    • A scorer run that ends without its output before its log records "full-support cells" (the stage just before the first candidate gap) has computed no candidate outcome.
    • A scorer run that ends without its output after its log records "full-support cells" is a spent attempt. It is recorded as ABORTED_AFTER_GAPS, a non-pass with FAIL's consequences. The log's last stage is published on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42, and the attempt is counted on the cumulative ledger (design line 469). Any successor needs a new registration. Section 8 forbids any rerun after a candidate outcome.

    Memory. The floor artifact publishes L before the scorer runs. A design replicate copies each truth and draw frame about L times, so a replicate's memory grows with L. The scorer's worker count is 4 // L (4 at L = 1, 2 at L = 2, 1 at L = 3 or 4), and the operator posts it on #42 before launch. This keeps workers × L at most 4. Protocol v2's planning run, at the same scale, ran its fixed-law arm F with 8 workers, each copying its truth and draw frames once (L = 1), and its refit arm with 6. Its record is docs/design/track_b_b0_1_planning_values.json, bootstrap.workers.

    Above L = 4 the scorer is not launched. Not launching is not an attempt and not an outcome. B2 stays unadmitted, and Max is asked about memory or the host. At the 2006 pseudo-origin every stratum had two clusters, so L was 1.

    Results do not depend on the worker count: a replicate is a function of its index and the payload alone, and replicates come back in index order (b2.run_replicates). tests/test_track_b_b2_rules.py checks design multiplicities and a toy function at 2 and 3 workers against a serial run. Scored gaps at 4 workers are not tested separately.

    Runtime

    All paths are on the operator's host.

    • Code. The B2 implementation merged by PR Track B B2: floor builder, comparator and scorer (invented-data build) #526, at 22e3f82ba876526054b46f7f484db4df04c374be.

      File SHA-256 at 22e3f82b
      src/populace_dynamics/track_b/b2.py 3390a4023041eee697df174322ede3e3f7d82e65f13a815908b09ca675bce51a
      scripts/track_b_b2_floor.py c92198f49aed0dd74c67a8135f12984022f45c971f94ee0caf25f81479618c44
      scripts/track_b_b2_compare_floor.py 98aa90cf890e5d99835802cc0b0fbb8dc19ea06d8e0f3cfbe9fa63699319deb6
      scripts/track_b_b2_score.py acf4fbcf5fdcda919d8303e0b2c0bcedd5fd17f73e2fc4f89229597ee112e176
      scripts/track_b_b0_1_planning_values.py 62bd282c4a2968e388e190806d8e0ad8612fcbcbd9564f5485d3e49752ec7c27
      scripts/track_b_b0_1_ruled_power.py 8cc64235f5157c13e99e14bd892317286a2e7f22a69dfb7443c7610c53f60af4
      docs/design/track_b_b0_1_planning_values.json f8a29eee25d721d30422c936d3e19fff60a93f817e70b76e9cb74c866a67d897
      docs/design/track_b_b0_1_counts.json e93826d4c535a41ea28155b837307a1a941f9ac9d205f8e8bf60421481e54f99

      The addendum and audit are unchanged at 22e3f82b; their hashes are in the table above.

    • The run clone: /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics, on branch trackb-b2-run-20261015, cut from 22e3f82b. Nothing else lives in it, so the scripts' clean-worktree check sees only the repository.

    • The runtime: /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2, outside the clone, with Python 3.14.4.

      • Its distributions are the 164 pins in runtime-requirements-pins.txt (SHA-256 19e95ff7e9d6fc771e8a1b88285beae6ff10ad9e8e77fa24735d944caecec7f3), the same pins as .venv-gate, the environment B2's tests ran in. policyengine-us 1.752.2 is among them, installed from its wheel.

      • populace-fit and populace-frame 0.1.0 are editable installs from /Users/maxghenis/PolicyEngine/_trackb-b2-run/populace-pinned. That checkout sits at the selectors' pinned head af02c917fcb3c50816bf3af9c2b64509e928889a, with fit tree 5c866378fdf5906b7a61da9977b8d028d1d36e9f and frame tree 7cfb9ee78beb74911963913f202a4471aae2f52b.

      • The floor builder and the scorer refuse any Python other than 3.14.4; any version of numpy, pandas, scikit-learn, scipy, quantile-forest, populace-fit or populace-frame other than the pinned one; and any populace head, tree or unclean source other than the pins. This is the planning script's runtime_record, called after their thread, registration, clean-worktree and output checks.

      • The other pins are checked by the operator. Before each launch, runtime_check.py (SHA-256 348fc70c5600149140d9a3b27d2de44c9bcb52f988c36e5a73dd84a363ab7042) runs from outside the clone, with absolute paths:

        cd /Users/maxghenis/PolicyEngine/_trackb-b2-run && \
          /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python -I \
          <evidence>/runtime_check.py <evidence>/runtime-requirements-pins.txt \
          /Users/maxghenis/PolicyEngine/_trackb-b2-run/populace-pinned
        

        <evidence> is this registration's folder on the operator's host; nothing is copied into the clone.

        • The check compares every installed distribution with the pins and flags any installed twice. It checks the populace checkout's head, trees and clean source, and that populace is imported from it.
        • It prints the interpreter, the prefix, the pins file's SHA-256 and its own.
        • Its output is posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42, and must end RUNTIME OK.
        • At registration it printed: executable /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python; pins_sha256 19e95ff7…; check_sha256 348fc70c…; distributions 166 (the 164 pins plus the two editable populace installs); listing_sha256 b774409c651ddfdd75eed26b35a1aad2e58ab1a2a07e94ff3b537350088d2986; RUNTIME OK.
    • policyengine-us 1.752.2, for the 2010 NAWI read only.

      • POPULACE_DYNAMICS_PE_US_DIR=/Users/maxghenis/PolicyEngine/_trackb-b2-run/pe-us-1.752.2 holds policyengine_us-1.752.2.dist-info/ and policyengine_us/parameters/gov/ssa/nawi.yaml, taken from the 1.752.2 wheel. The file's hash matches the wheel's RECORD entry sha256=nbVf6eolRHoTl2fHcZsykgmC3SpliHyPwN2xKi3PfFM (SHA-256 hex 9db55fe9ea25447a139767c7719b32920982dd2a65887c8fc0ddb12a2dcf7c53). The operator checked this; the code does not.
      • The scorer refuses any other version, and any NAWI prefix that differs from the selectors' 2010 pins: 1,588 bytes, prefix SHA-256 e272a7bc…, mapping SHA-256 26b6a049…. The read passed at registration.
    • Threads, jobs and workers.

      • Every process runs one BLAS, OpenMP and numexpr thread: OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS and NUMEXPR_NUM_THREADS are all 1. It runs with OMP_WAIT_POLICY=PASSIVE and POPULACE_FIT_N_JOBS=4, and with the worker count set by the memory rule above. That stays within Max's 2026-10-11 cap for the shared host.
      • The floor builder and the scorer refuse any other thread setting, a POPULACE_FIT_N_JOBS outside 1 to 4, or more than four workers. They check this first, before reading anything. The comparator has none of these guards; its only check is that the floor artifact is committed.
      • Candidate 3's own frozen runner pinned eight threads and ACTIVE. B2 refits and projects candidate 3 at one OpenMP thread instead, through the selectors' _project, as protocol v2's planning run did. That run's record shows OMP_NUM_THREADS=1 and POPULACE_FIT_N_JOBS=4.
      • Why one thread. Two environments are involved:
        • .venv-gate, the environment B2's tests ran in, at ~/PolicyEngine/_trackb-b2-build-r3/microcosm-dynamics/.venv-gate. Its populace is the shared checkout ~/m6-sol-lanes/populace-pinned, whose fit and frame trees equal the pins. On 2026-10-15 that checkout stood at head ee8f7fc; its head on 2026-10-13 was not recorded.
        • .venv-b2, the registered runtime, with the same 164 pins and populace at the pinned head af02c917.
      • On 2026-10-13, in .venv-gate, probes of candidate 3's fit and projection on invented data were run with more than one OpenMP thread (2 and 4 threads, PASSIVE and ACTIVE). No logs of these probes were kept; this account is from the session record.
        • One, run under pytest, sat at 0% CPU for more than ten minutes. Its sampled stack had the main thread in a pandas property lookup, with numexpr's worker threads idle.
        • Six were stopped by timeouts: one at 120 s, two at 150 s and three at 60 s. The 120-s and 150-s tracebacks were inside the participation gates' HistGradientBoostingClassifier.predict_proba. The 60-s tracebacks were not read.
        • Those timeouts are close to the times a 2- or 4-thread probe takes when working normally (40-59 s on 2026-10-15). So only the 0%-CPU run shows a stall.
      • On 2026-10-15, at 22e3f82b, the same probe was run. It is _probe in tests/test_track_b_b2_threads.py, called with b2.thread_settings stubbed to allow more threads.
        • In .venv-b2 it completed at 1 thread in 16 s; at 2 threads, PASSIVE, in 40 s; at 4 threads, PASSIVE, in 51 s; and at 4 threads, ACTIVE, in 19 s.
        • In .venv-gate it completed at 4 threads, PASSIVE, in 59 s.
        • The timings and settings are in thread-hang-reproduction/summary.txt; each log holds the probe's output.
      • The cause of the stall was not identified. B2 uses one thread because it completed in every run, and its cross-process determinism is tested.
      • b2.thread_settings' docstring, and the comments where the floor builder and the scorer call it, describe the stall as if it always occurs. This account supersedes them.
      • Forest seeds are per tree, so the forest job count is not expected to change a fit. B2 fixes it at 4 and does not rely on that expectation.
    • Determinism. tests/test_track_b_b2_threads.py was run at 22e3f82b in this runtime. Two fresh processes with different hash seeds give:

      • an equal fitted state, field by field, leaving out only _BinMapper.n_threads, the thread count HistGradientBoosting records;
      • equal gate probability surfaces;
      • byte-identical draws for all 20 seeds.

      2 passed (threads-determinism-22e3f82b-venv-b2.log). CI lacks populace.fit, so it skips that test.

    Procedure

    --registration-id is this comment's numeric GitHub comment id. Every #42 post below is a new comment. The operator is the registering session, named under "Exposure" below.

    1. Runtime check. The operator runs runtime_check.py as above and posts its output on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42.

    2. The floor builder runs once, from the clean run clone at 22e3f82b:

      cd /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics
      PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS='ignore::FutureWarning' PYTHONPATH=src:scripts \
        OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 VECLIB_MAXIMUM_THREADS=1 \
        NUMEXPR_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE POPULACE_FIT_N_JOBS=4 \
        POPULACE_DYNAMICS_PSID_DIR=/Users/maxghenis/PolicyEngine/psid-data \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python scripts/track_b_b2_floor.py \
        --registration-id <this comment's id> --expected-commit 22e3f82ba876526054b46f7f484db4df04c374be --workers 4
      

      It writes runs/track_b_b2_floor.json exclusively. That file alone is committed, as the only change, in one commit whose parent is 22e3f82b, and pushed to trackb-b2-run-20261015. The commit id and the artifact's SHA-256 are then posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42. The artifact holds:

      • the gated and report-only cells, with exposure labels and plain-unit tolerances;
      • the strata before and after the merges, the merges, L and ν;
      • the ladder and the power;
      • each cell's household-replicate undefined share;
      • the person-split hashes.

      If it records WEAK_POWER_OR_VACUITY, or rule 7 or the memory rule stops B2, B2 stops there and returns to Max. The comparator still runs, because it gates nothing.

    3. The comparator runs once, at the floor commit and before the scorer, with its default ledgers and no --ledger. Its code is the version at 22e3f82b, since the floor commit differs from 22e3f82b only by the artifact:

      PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src:scripts \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python scripts/track_b_b2_compare_floor.py
      

      All four booleans it prints (two fields for each of the two boundary-2010 selection ledgers) are posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 whatever they are. A mismatch or an error is published and gates nothing. The run is recorded on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 as the operator's ledger read.

    4. The scorer runs once, from the clean run clone at the floor commit. The operator first checks, and posts, that:

      • the floor commit has a single parent, 22e3f82b (git rev-parse <floor commit>^@ prints only it), and its only change is A runs/track_b_b2_floor.json;
      • the eight file hashes in the Runtime table hold at that commit;
      • runtime_check.py still ends RUNTIME OK.

      The scorer gets the same environment as the floor builder, plus POPULACE_DYNAMICS_PE_US_DIR=/Users/maxghenis/PolicyEngine/_trackb-b2-run/pe-us-1.752.2. Its arguments are the same --registration-id, --expected-commit <the floor commit>, and the worker count set by the memory rule. --floor-artifact is not used, so the scorer reads the default runs/track_b_b2_floor.json.

      Its output runs/track_b_b2_score.json and its attempt log are created exclusively, then committed and pushed to trackb-b2-run-20261015. After any candidate outcome there is no rerun, new seed, larger K, re-pruning, tolerance change or rescoping (addendum section 8).

    5. The outcome is published on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 whatever it is, with every gated and report-only cell scored, and the attempt goes on the cumulative ledger (design line 469). The run branch is then merged to master by a PR that adds only the run's files.

    Exposure of the registering session

    This registration was written by the session that drove Track B's B2 chain (Claude, Opus 5.5). In preparing it, the session read the audit's frozen support counts for 2012 and 2014. These are rows present by cohort and rows PSID assigned or edited: presence counts, published in the audit. It has met no 2012 or 2014 earnings statistic, floor value or B2 candidate output, and no selector outcome at 2008 or 2010. B1's raw output and sealed logs, which hold reference-year 2014 values, stay unopened until B2's result is published. This session is also the operator. Its comparator run (step 3) reads two fields of the boundary-2010 selection ledgers and prints only booleans; it is recorded as this session's ledger read.

    Review before posting

    Each round reviewed a cleaned copy of the repository at 22e3f82b, with the Q6 exclusion list removed. Subfleet routed each review job to a model: rounds 1 and 2 ran on Opus 5.5, round 3 on GPT-6.1 Sol.

    • Round 1 (Opus 5.5) requested changes: two high, eight medium and twelve low findings.
    • Round 2 (Opus 5.5) requested changes: six medium and nine low.
    • Round 3 (GPT-6.1 Sol) left one low finding, a stale worker-count phrase, which is deleted here. It verified every quoted file hash, the runtime listing's 166 distributions and its hash.

    All three found no blinded value in what they read.

  11. MaxGhenis commented on Oct 15, 2026

    @MaxGhenis
    ContributorAuthor

    B2, step 1: runtime check (registration: #42 comment 6199723681).

    Run with the registered runtime's python, from outside the run clone. The run clone is clean at 22e3f82ba876526054b46f7f484db4df04c374be.

    Thu Oct 15 18:47:34 UTC 2026
    executable /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python
    prefix /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2
    python 3.14.4
    pins_file /Users/maxghenis/microcosm-launch-evidence/dynasim-parity-20260909/trackb-b2-registration-20261013/runtime-requirements-pins.txt
    pins_sha256 19e95ff7e9d6fc771e8a1b88285beae6ff10ad9e8e77fa24735d944caecec7f3
    check_sha256 348fc70c5600149140d9a3b27d2de44c9bcb52f988c36e5a73dd84a363ab7042
    distributions 166
    listing_sha256 b774409c651ddfdd75eed26b35a1aad2e58ab1a2a07e94ff3b537350088d2986
    populace_head af02c917fcb3c50816bf3af9c2b64509e928889a
    RUNTIME OK
    exit 0
    

    The floor builder (step 2) starts next.

  12. MaxGhenis commented on Oct 15, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2, steps 2 and 3: the floor stops B2 at floor build (WEAK_POWER_OR_VACUITY). Registration: comment 6199723681.

    Step 2: the floor builder

    • How it ran. Once, at 18:49Z, from the clean run clone at 22e3f82b, using the registered command and environment: 4 workers, one BLAS/OpenMP/numexpr thread, OMP_WAIT_POLICY=PASSIVE, POPULACE_FIT_N_JOBS=4.

      • It wrote its artifact at 18:53Z, and the process then exited.
      • It ran under nohup and was watched by PID, so its exit code was not captured.
      • Its combined stdout and stderr hold only stage lines, with no warning or error: the read set under the read guard; the household-split floor, seeds 0-99; the person-split floor, seeds 0-99 (non-gating); the household bootstrap, 4,000 replicates; and the write.
    • Commit. 7fdc8f91c11a0d251efb399f9490fbdecc5a9bc3 on trackb-b2-run-20261015, pushed.

      • Its single parent is 22e3f82b, and its only change is A runs/track_b_b2_floor.json.
    • The artifact.

      • SHA-256 500f801e0cad5be0aebaaa0704e8fa21bdc6b2d7ead50f512abe2953c5f93aaf.
      • Fields: outcome: WEAK_POWER_OR_VACUITY, candidate_runs_permitted: false, worktree_clean: true.
    • Read set. The builder opened 78 of the 80 pinned files, each with its pinned hash and none outside the set. The two it did not open are FAM1968.txt and FAM1968.sps.

    • Design.

      • Before any merge: 63 strata, each with two clusters.
      • So there were no merges, L = 1 and ν = 63.
    • Eligibility. All six ruled cells are eligible.

    • Ladder. It ran inside the six, in order of descending tolerance/σ on the household-split floor (section 5):

      Step Cell Action Gate power after
      before pruning the six 0.557
      1 earn_p90.prime pruned 0.604
      2 earn_zero_rate.prime pruned 0.672
      3 earn_zero_rate.older kept, last of the zero-rate family
      4 earn_p50.prime pruned 0.797
      5 earn_p90.older pruned 0.862
      6 earn_p50.older kept, last of the log-quantile family
      • The ladder ends at 0.862 with two cells, earn_p50.older and earn_zero_rate.older, at t*₂,₆₃ = 2.296.
      • The guards read at_least_four_cells: false and not_every_tolerance_at_cap: true.
      • So it fails twice over: power is below 0.90, and fewer than four cells remain.
      • surface.gated is empty, and the headline counts zero gated cells.
    • Rule 7. cells[c].se_up.undefined_share_ok is true for all six ruled cells. surface.gated is empty, so these are the six ruled cells.

    • Memory rule. L = 1 would give 4 workers. It does not apply, because the scorer is not launched.

    What follows. Under the registration's element 6, first case, B2 stops at floor build, before any candidate run.

    • The scorer is not launched, so no attempt log and no candidate outcome exist.
    • Options B-D of d1342 return to Max, priced on B2's real tolerances.
    • No rescoped or amended registration is written before he rules.

    Step 3: the comparator

    • How it ran. Once, at the floor commit 7fdc8f91, with its default ledgers and no --ledger.

      • Its code is 22e3f82b's.
      • Exit 0, with empty stderr.
    • Output. Four booleans, all true:

      Ledger Six-cell floor block Seed detail
      docs/analysis/m6_qstar_train_only_selection_results.json true true
      docs/analysis/m6_rhostar_train_only_selection_results.json true true
    • Meaning. B2's non-gating person-split floor block and seed detail for the six cells equal both boundary-2010 selection ledgers', by canonical SHA-256. This gates nothing.

    • This run is the operator's ledger read. It read those two fields and printed only these booleans.

    Blindness: no 2012 or 2014 floor value, tolerance or truth moment is quoted here. They are in the committed artifact. No candidate outcome exists.

  13. MaxGhenis commented on Oct 15, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2: d1342's options B-D priced on B2's real floor. This is decision input, not a registration. It follows step 2 (comment 6200099988).

    B2 stopped at floor build, so under the registration the choice among d1342's B-D returns to Max, priced on B2's real tolerances. It is queued as d1606. No rescoped or amended registration is written, and no candidate is run, before he rules.

    Method.

    • The pricing calls the floor builder's own power function (track_b_b2_floor.ladder_power): the ruled binding evaluation with B2's τ = tol/se_up, ν = 63, and the registered planning values.
    • It runs against the floor artifact at 7fdc8f91.
    • It first reproduces the committed ladder exactly (11 checks).

    Power. Binding evaluation / most lenient evaluation; 0.90 is the bar.

    Option At k = 3 Ladder at k = 3 Smallest k reaching 0.90
    A, the six (as run) 0.557 / 0.718 Ended at two cells, 0.862: fails —
    B, drop earn_p50.older 0.791 / 0.892 Prunes to two cells: fails 3.34 (tolerances about 8-9% wider): 0.903 / 0.957
    C1, drop both medians 0.923 / 0.968 Clears with no pruning 3
    C2, drop earn_p50.older and earn_zero_rate.older 0.853 / 0.924 Prunes to two cells: fails 3.18 (about 4-5% wider): 0.9000007 / 0.952
    D, the six, widened at k = 3.26 (as priced on 10-11): 0.697 / 0.824; the ladder fails — 3.85 (about 19-24% wider): 0.902 / 0.954
    • Surfaces at k = 3. Of the 21 surfaces of four or more of the six cells that keep both families, only C1 reaches 0.90.
    • Caps. No tolerance reaches its cap at any k shown.
    • Fixed-surface power never falls as k rises.

    Sensitivity. This is a hypothetical stress, not a confidence interval. The design ratio d is transported from the 2006 pseudo-origin and is uncertain by roughly ±9% (addendum section 10). Scaling every retained cell's d by one common factor:

    Option, at its setting d × 0.91 d × 1.00 d × 1.05 d × 1.09
    C1, k = 3 0.964 0.923 0.888 0.854
    B, k = 3.34 0.955 0.903 0.861 0.819
    C2, k = 3.18 0.950 0.900 0.861 0.823
    D, k = 3.85 0.952 0.902 0.861 0.822

    C1 stays at or above 0.90 up to d × 1.034.

    Review. GPT-6.1 Sol reviewed the pricing in two rounds.

    • Round 1 requested changes to wording and disclosure. It found no computational defect.
    • Round 2 approved. Both rounds reran the scripts and got byte-identical outputs.
    • In both rounds, the reviewer disclosed that one of its own diagnostic commands echoed tolerance fields from the committed artifact into its session. Its reports quote none.

    Not quoted here. No floor value, tolerance or truth moment appears in this comment; they are in the committed artifact. No candidate outcome exists.

  14. MaxGhenis commented on Oct 16, 2026

    @MaxGhenis
    ContributorAuthor

    Erratum note: the delayed-retirement-credit window (#494), labelled diagnostic under ruling d1408. No registered artifact was rerun or replaced.

    ss.benefits.delayed_credit capped delayed credits at 48 months. For births 1917–1942, the statutory window (42 USC 402(w)(2)(A)) is 50–60 months. #532 added the statutory function beside the sealed oracle.

    This diagnostic rebuilt each reachable result's admitted records with the code at its recorded commit. For five results it reproduced the committed model headlines under the legacy cap, then switched only the credit window. First estimates is bounded, not reproduced. The note reports counts and changes only; cells under ten people are withheld.

    Result Legacy reproduction Flagged (1917–1942, late) Change under the statutory window
    Track M minimum benefit exact 34 people, 1.72% of admitted weight none
    R7 earnings sharing exact 12 recipients via linked survivors, 2.06% aggregate cost 0.0 pp; widowed cells withheld
    Cost ordering (R7 part) exact as R7 0.0 pp
    Track A COLA exact 75 (2011 cohort), 62 (2009) 0.0 below age 70, and at 70–79 in R5; other older cells withheld
    FRA 68 exact withheld (shares COLA's cohorts) 0.0 below age 70, and at 70–79 in F6; other older cells withheld
    First estimates (#307) not computed: the registered fitting stack is not installed in the diagnostic environment, and the projection was not refit at most 21 people, ≤1.65% of each draw's admitted weight not computed

    No pre-1917 record was flagged. For any one supported worker, the added gross delayed credit before payment rounding is at most 6.5 percentage points of PIA (1937 births, claim at 70). Every registered result stands as registered. Later statutory work selects DelayedCreditWindow.STATUTORY.

    Details: docs/errata/2026-10-11-delayed-credit-window.md and docs/errata/2026-10-11-delayed-credit-window-diagnostic.json (merged in #538 as b3ba7e2).

  15. MaxGhenis commented on Oct 18, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2: rescoped registration under d1606 (option C1)

    Status. This is a registration, posted on issue #42 before B2's rescoped floor build and before any candidate run. No B2 candidate outcome exists. B2's first floor build stopped B2 before any candidate run (below).

    What it replaces.

    • It supersedes B2's first registration (comment 6199723681) from the floor build onward.
    • Everything that registration fixed and this one does not change is restated here, so this text stands alone.
    • The changes are:
      • the gate surface;
      • the headline;
      • the code commit and its hashes;
      • the run clone;
      • the floor rerun and its reproduction check;
      • the exposure account.

    Verification class: retrospective regression (design §3.2). Admitted scope: "2010→2014 continuity on the stated surface, ages 25–64. Not a 2010-vintage forecast" (design line 150).

    Headline (d781 (a), d1606 C1). B2's gate covers the p90 of annual earnings and the zero-earnings rate, by cohort, only. It does not cover median earnings, changes, dispersion, mobility or persistence.

    • H and B3L rely on B2 for those two level statistics only.
    • The other twelve cells, both medians among them, are scored and published report-only.

    Median earnings. B2 certifies no median earnings level, so H and B3L cannot rely on B2 for one. The medians are an open item, to be settled when B3L's surface is frozen at B0.2. This is not an assignment:

    • the design gives B3L no median-earnings test;
    • B3L's surface is not yet frozen.

    Max confirmed this wording on 2026-10-16, after a correction (d1606, note of 2026-10-16).

    2012 and 2014 informed earlier selection. Every evaluation row of B2 lies in years the B0.1 audit lists as exposed (audit §5). They were selection evidence for candidate 3's law, and boundary-2008 and boundary-2010 candidate results were published (PRs #255 and #273).

    • EXPOSURE_UNRESOLVED therefore fires, and this registration is the registered fallback at the §3.2 scope above (d693 Q8 (b)).
    • H and B3L may rely on B2 only within that scope.
    • Removing this statement after any candidate run requires a dated amendment.

    How B2 got here

    1. B2 registered on 2026-10-15 (comment 6199723681) under d1342 (A): gate the six level cells, and let the floor's ladder decide.
    2. Its floor builder ran once (comment 6200099988), from 22e3f82b. B2 stopped at floor build, before any candidate run, as WEAK_POWER_OR_VACUITY.
      • The artifact was written and the process exited, but its exit code was not captured: it ran under nohup and was watched by PID.
      • The ladder inside the six ended at two cells, earn_p50.older and earn_zero_rate.older, at power 0.862.
      • That is below 0.90 and below the four-cell guard.
      • Nothing else stopped it: ν = 63 with no merges, L = 1, every undefined share within 1%, and the comparator's four booleans true.
    3. d1342 returned the choice among its options B-D to Max, priced on B2's real tolerances (comment 6201696504; the decision aid was reviewed by GPT-6.1 Sol in two rounds and approved).
      • At the registered k = 3, only C1 cleared: the six less both medians, at 0.923.
      • B, C2 and D each needed a wider tolerance.
    4. Max ruled d1606 on 2026-10-16: C1. The ruling text reads: "drop both medians (earn_p50 prime and older); keep k = 3." The exact surface is the decision aid's option C1.
      • He was first told that B3L would cover the medians. That was wrong: the design gives B3L no median-earnings test, only reliance on B2.
      • Shown the correction, he kept C1, with the medians as an open item for B0.2 (d1606, note of 2026-10-16). The alternatives he was offered were C1 without that handoff, and B at k = 3.34.
    5. PR Track B B2: gate surface C1 (d1606) #545 implemented it (merged as 892040c88a6a2342d5545b8379920e077e0f4609). It adds b2.GATE_SURFACE and runs eligibility, the stop checks, the ladder and the guards inside it. It also:
      • makes the scorer refuse any gated cell outside it;
      • moves the floor schema to v2, so the scorer refuses the first floor;
      • states the new headline.

    The first floor build is not a candidate attempt. It ran no candidate. Its artifact stays on trackb-b2-run-20261015 at 7fdc8f91 (SHA-256 500f801e0cad5be0aebaaa0704e8fa21bdc6b2d7ead50f512abe2953c5f93aaf). Step 5 adds it to master as runs/track_b_b2_floor_v1_stopped.json, unchanged.

    The frozen record

    B2 registers on the B0.1 addendum and audit, as merged to master by PR #505 at 3d23760d:

    File SHA-256 at 3d23760d
    docs/design/track_b_b0_1_addendum.md f719edd9bdfc969c014f2bf2d7b854d2913da9b4225d64a84a78fa53c909cd12
    docs/design/track_b_b0_1_audit.md 16b029728cfd09ef1c879bb9419cba2ec2c5dc92d6f0355498897c11c8fbb128

    At 892040c8 the addendum's SHA-256 is 8896e8a7c183de1e6f9d8123ae6d9968f0c9d90f915472aa434c6c72b3d4f11c. PR #534 (read log v7) changed section 15 only. Section 16 starts at line 1865, and its protocol block's SHA-256, which the floor builder checks, is unchanged at 2a1681cdb3b6e28f30a8c1a319895b95f25c2fb27a175c50ded8a196d8488fe9. The audit is unchanged.

    Rulings carried.

    • d693: the audit's eight defaults, with riders.
    • d622: a B1 v2 pass may unlock B2, with the weaker label carried.
    • d765: the refit arm's household half-samples.
    • d781 (a): the six level cells are the ruled surface.
    • d782: Rao-Wu design-cluster standard error.
    • d1342 (A): proceed as ruled; B2's floor-build ladder decides; a failure returns options B-D to Max on B2's real tolerances.
    • d1606 (C1): the gate surface is the six less both medians, at k = 3.

    Inherited evidence

    B1 v2 passed as a reconstructed reproduction (weaker than bit-for-bit) (d622). It returned RECONSTRUCTED_REPRODUCTION, with zero differences on all four conditions.

    Candidate 3's disclosure, in full (design lines 29–31):

    Candidate 3 records a valid M6 pass; it certifies nothing about mortality drift. The artifact records four passing seeds, q=0.55 and rho=−0.6. Fits use data through 2014; gated demographic flows cover 2015–2019 and earnings cover 2016/2018. The 2020–2022 window is diagnostic only. Certification is conditional on prior structural and same-holdout surface selection. The v4 point estimate p_gate=0.9018301 clears 0.90 by 0.0018301, but its clearance is not statistically resolved: the reported jackknife SE is approximately 0.013–0.019. Synthesized support, redrawn initial states and endogenous widowhood require the decision-5 successor gate.

    Elements

    1. Source release. The PSID files B2 reads are pinned by the B0.1 counts record's b2_read_set_sha256: 80 files, the .txt and .sps of every family wave 1968–2015, plus IND2023ER.txt and IND2023ER.sps.

    • Its canonical SHA-256 (sorted keys, compact separators) is 787725e30d2273a76854e134a41b882110c76e222925ea8022ac891f413fb62c.
    • The floor builder refuses to open any other PSID file, and checks every opened file's hash against the pin. The first floor opened 78 of the 80; it did not open FAM1968.txt and FAM1968.sps.
    • Wage vintage: policyengine-us 1.752.2 nawi.yaml, realized through 2010, with later years from the fitter's log-linear projection over 2001–2010.
      • It is labelled pe_us_1_752_2_nawi_realized_2010_loglinear_2001_2010, not ols_log_nawi_2005_2014.
      • It is the series as later revised, not a 2011-era release.

    2. Variables and the codebook concept check (audit §4.2, definitions only).

    • All six labor-income variables define the concept as labor income "Excluding Farm and Unincorporated Business Income", and the concept reads the same across B2's window.
    • The one difference: market gardening is absent from the 2015 head entry's component list. Whether the 2015 instrument dropped or moved it was not checked.

    3. Population. The 2011 anchor persons with a valid 2010 row: 13,542 persons.

    • The floor builder defines them from data, and checks them against the frozen count.
    • No one is scored outside ages 25–64 at the scored waves.
    • B2's bridge hands this domain to H (d693 Q7).

    4. Exposure history. As stated above. Each gated cell carries an exposure label, and the floor artifact's headline counts them.

    • earn_zero_rate.older carries "published candidate-3-law outcomes at boundaries 2008 and 2010".
    • The other three gated cells carry "published five-seed floor detail, no published candidate outcome".

    5. Holdout partition.

    • Estimation: earnings rows with reference year 2010 or earlier, fitted at ages 25–64.
    • Evaluation: realized 2012 and 2014 rows on the frozen 2010 domain, at collection-wave ages 25–64.
    • Split unit: the 2011 household, for floor seeds 0–99 and gate seeds 0–4.

    6. Critical cells (d781 (a), d1342 (A), d1606 (C1)).

    • The gate family is earn_p90.older, earn_p90.prime, earn_zero_rate.older and earn_zero_rate.prime (b2.GATE_SURFACE).
    • Report-only: earn_p50.older and earn_p50.prime, with the reason "outside the rescoped surface (d1606 option C1)", and the other ten cells, as before.
    • The floor builder applies eligibility and M6's ladder inside the gate family, on B2's own floor:
      • Order the eligible cells by descending tolerance/σ, ties by name. σ is M6's realized σ, √(mean² + SD²) of the 100 floor seeds' scores (m6_cells.py:681), not their SD.
      • Compute gate power by the ruled binding evaluation (scripts/track_b_b0_1_ruled_power.py), with the critical value t*_{m,ν}.
      • Stop at power 0.90 or more. Never prune a family's last cell.
      • At least four cells must remain, and not every tolerance may sit at its cap.
    • What that means at four cells. The family has exactly the four cells the guard needs, so any prune fails the guard, and the ladder can only confirm that the four reach 0.90.
    • What the first floor priced. On the first floor, the four reach 0.923 on the binding evaluation and 0.968 on the most lenient, and no tolerance sits at its cap (comment 6201696504). The rerun recomputes this; it is not carried as a number.

    Either of two cases makes B2 WEAK_POWER_OR_VACUITY at floor build, before any candidate run, and B2 stops there:

    • The ladder or a guard fails.
    • ν is 0 after the stratum rule's merges, or an eligible gate-family cell's σ, se_up or τ is zero or undefined.

    Either way, B2 returns to Max. No rescoped or amended registration is written before he rules.

    7. Tolerances. tol_c = min(round(mean_c + 3 SD_c, 3), cap) over the 100 household-split floor seeds; the caps are ln 1.5 and 0.15.

    • The floor artifact states each gated tolerance in plain units before any candidate outcome.
    • §5.3's earnings limits do not apply to B2: no Track B gate holds ages 25–61 earnings to a fixed limit (d693 Q4).

    8. Uncertainty.

    • se_up: 4,000 household-bootstrap replicates, × √(1 + 1/K).

    • se_boot: 4,000 design replicates, Rao-Wu within strata, with the law and the paths fixed. The stratum rule (addendum section 7):

      • two-cluster strata follow arm D's rule;
      • one-cluster strata merge into the nearest stratum by code among those with two or more clusters, the lower code on a tie. All merges are applied together, before anything else. If no stratum has two or more clusters, all merge into one, which takes the lowest original code;
      • strata of three or more clusters get exact Rao-Wu at scale L, drawing rng.multinomial(n_h − 1, 1/n_h) over their clusters sorted by original (stratum, cluster). Strata are visited in ascending merged code, all at the commit pinned below;
      • ν is counted after the merges.

      The floor builder publishes the strata by cluster count, the merges, L and ν before any candidate run. The first floor found 63 strata of two clusters, so no merges, L = 1 and ν = 63.

    • se_c = √(se_boot² + a_c se_up²). a_c is the cell's registered estimation-variance ratio: e_B2 from the planning record, plus the transported cross term f × c where protocol v2's rule adds it.

    • Bound: |g_c| + t*{m,ν} se_c ≤ tol_c, where t*{m,ν} = t_ν⁻¹(1 − 0.05/(2m)). At m = 4 and ν = 63 it would be 2.571; B2 uses its own m and ν.

    • Monte Carlo rule: the per-draw signed gap's SD / √K must be below tol_c/10.

    • Undefined replicates: a cell with more than 1% of its replicates undefined cannot pass.

    9. Pass conjunction. A gated cell is valid on a frame when its truth is defined, none of its K draws is undefined, and the draws are not all identical. A gate seed passes when every gated cell's side-A gap lies within tol_c. The outcome is the first that applies:

    1. INVALID: a gated cell invalid on full support or on a gate seed's side A.
    2. FAIL: fewer than four of five gate seeds pass, or some |g_c| > tol_c.
    3. PASS: every bound within tolerance and every precision rule holding.
    4. INCONCLUSIVE: otherwise.

    INVALID and INCONCLUSIVE are non-passes with FAIL's consequences.

    10. Reference dates versus collection (audit §4.3).

    Quantity Reference year Collected at
    Anchor earnings 2010 2011 interview (FAM2011ER)
    Horizon earnings 2012 2013 interview (FAM2013ER)
    Horizon earnings 2014 2015 interview (FAM2015ER)

    Age and weight are measured at the collection wave. PSID has no 2012 or 2014 interview. All staged files are retrospective releases, made 9 to 13 years after their reference years.

    Seeds and streams.

    • The scoring root is 20261002.
    • Household replicate b uses stream [root, b, 0], and design replicate b stream [root, b, 4].
    • Candidate 3's law is fitted with seed 5200 and projected with draws 6200–6219 (K = 20).
    • Floor seeds are 0–99, and gate seeds 0–4.

    Planning values. They come from a 2006 pseudo-origin, whose truth years, 2008 and 2010, fall in a recession. Neither their transport to 2012 and 2014 nor r's is tested. The decision aid's hypothetical stress showed C1 stays at or above 0.90 until the design ratio is about 3.4% above its registered value.

    Rules the implementation adds to the addendum's text

    Rules 1–9 were chosen in review before the first floor build. Rules 10 and 11 were added with this rescoping: after that build, and before any candidate run. All are stated here, so that nothing is decided after a candidate outcome:

    1. Copies past 999. The planning script's copy_persons numbers copies person × 1,000 + k and refuses 1,000 or more.

      • Below that, B2 calls it unchanged.
      • Above it, which only a large enough stratum-rule scale L can produce, B2 runs the same construction with a wider power-of-ten identifier scale. Only the identifiers' encoding differs.
    2. Eligibility is section 5's exactly: every seed defined, at least 20 events in the weaker half, and planning values present. A gate-family cell that fails it is scored report-only, and the ladder runs on the others.

    3. An eligible gate-family cell with zero or undefined σ, se_up or τ has an uninformative tolerance or no computable power, and section 5 never drops an eligible cell. So B2 stops at floor build as WEAK_POWER_OR_VACUITY. A report-only cell's undefined value stops nothing.

    4. Replicates that raise count as undefined for every cell, with the exception class recorded. A memory or operating-system error stops the run.

    5. An undefined truth makes the cell invalid on that frame, as in M6.

    6. The report-only interval is g ± t_ν⁻¹(0.975) se_c.

    7. More than 1% undefined household replicates in a gated cell is known at floor time, as cells[c].se_up.undefined_share_ok in the floor artifact, and that cell cannot pass.

      • The code does not apply this rule: such a floor still records outcome: LADDER_CLEARED. The operator reads undefined_share_ok for every cell in surface.gated and posts the values on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42.
      • If any is false, the scorer is not launched. By this registration B2's status is then WEAK_POWER_OR_VACUITY ("undefined cells", design line 454), whatever the artifact's outcome reads, and B2 returns to Max.
    8. The artifact's read_set_complete field is fixed at true in the code. It means only that every opened PSID file was in the pinned read set with its pinned hash. The files actually opened are listed with their hashes in psid_files_opened_sha256.

    9. "To B2" items carried in code and tests. They are in tests/test_track_b_b2_rules.py, tests/test_track_b_b2_design.py and tests/test_track_b_b2_pipeline.py:

      • the root-parameterised copies of anchor_multiplicities and design_multiplicities, called with root 20261002;
      • the differential test that, at root 20260930, they equal the planning functions for replicates 0-99;
      • the test that nothing in B2 reads BOOTSTRAP_ROOT_SEED;
      • the test that the scorer and arm F build identical copied frames;
      • the stratum rule's tests, including the differential with arm D at L = 1.
    10. The gate surface (PR Track B B2: gate surface C1 (d1606) #545).

      • b2.GATE_SURFACE is pinned to C1 by a test, and the published surface is property-tested: the floor gates only GATE_SURFACE cells and publishes every gateable cell exactly once.
      • The scorer refuses, before any fit, a floor that gates a median, any cell outside the six, or fewer than four cells, and any floor with schema v1.
    11. The floor rerun must reproduce the first floor's truth side. The floor builder is truth-only and deterministic. So, at this registration's commit, its rerun must produce truth-side fields equal to the first floor's, compared with compare_floors.py (below). Those fields are:

      • every cell's floor, eligibility, tolerance, cap flag, plain units, se_up, τ and a_c;
      • the design;
      • the domain;
      • the counts;
      • the person-split hashes;
      • the opened files.

      Only the schema, the surface, the ladder, the outcome, the power and provenance may differ. If any truth-side field differs, the scorer is not launched, and the difference is published and goes to Max.

    Interruption.

    • The attempt log. After every check that needs no PSID data, and before any PSID read, the scorer creates runs/track_b_b2_score.attempt.jsonl exclusively. It appends each stage there, synced to disk, and refuses to start if the file exists.
    • A refusal before the log exists reads no PSID data and is not an attempt, for example on a wrong POPULACE_DYNAMICS_PE_US_DIR. The cause is fixed and posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42, and the scorer is relaunched.
    • A floor build that ends without its artifact:
      • If it was interrupted (killed, out of memory, host failure), it may be rerun at 892040c88a6a2342d5545b8379920e077e0f4609, since it is truth-only and deterministic. Its stderr and the interruption are posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42. A partly written artifact is moved outside the clone, hashed and disclosed first.
      • If one of its checks refused it (frozen counts, read guard, protocol hash), the refusal is published and goes to Max. A deterministic refusal would only repeat.
    • A scorer run that ends without its output before its log records "full-support cells" (the stage just before the first candidate gap) has computed no candidate outcome.
    • A scorer run that ends without its output after its log records "full-support cells" is a spent attempt.

    Memory. The floor artifact publishes L before the scorer runs. A design replicate copies each truth and draw frame about L times, so a replicate's memory grows with L.

    • The scorer's worker count is 4 // L: 4 at L = 1, 2 at L = 2, and 1 at L = 3 or 4. The operator posts it on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 before launch. This keeps workers × L at most 4.
    • Above L = 4 the scorer is not launched. Not launching is not an attempt and not an outcome: B2 stays unadmitted, and Max is asked about memory or the host.
    • The first floor's L was 1.

    Results do not depend on the worker count: a replicate is a function of its index and the payload alone, and replicates come back in index order (b2.run_replicates). tests/test_track_b_b2_rules.py checks design multiplicities and a toy function at 2 and 3 workers against a serial run. Scored gaps at 4 workers are not tested separately.

    Runtime

    All paths are on the operator's host.

    • Code. PR Track B B2: gate surface C1 (d1606) #545's merge commit, 892040c88a6a2342d5545b8379920e077e0f4609.

      File SHA-256 at 892040c88a6a2342d5545b8379920e077e0f4609
      src/populace_dynamics/track_b/b2.py bdb1676d9a4a15c3ebc83d5f2c5e4643ed214012725373949fcfbca51aa587dc
      scripts/track_b_b2_floor.py 84b52fc7697e75c72e91ba5254e617e70c6be6e33508c50f405d3b9192120c7c
      scripts/track_b_b2_compare_floor.py 98aa90cf890e5d99835802cc0b0fbb8dc19ea06d8e0f3cfbe9fa63699319deb6
      scripts/track_b_b2_score.py fce4e580c5c1cdbc39107c4ae61d1f664684cf266fe3d003196b78efdac16526
      scripts/track_b_b0_1_planning_values.py 62bd282c4a2968e388e190806d8e0ad8612fcbcbd9564f5485d3e49752ec7c27
      scripts/track_b_b0_1_ruled_power.py 8cc64235f5157c13e99e14bd892317286a2e7f22a69dfb7443c7610c53f60af4
      docs/design/track_b_b0_1_planning_values.json f8a29eee25d721d30422c936d3e19fff60a93f817e70b76e9cb74c866a67d897
      docs/design/track_b_b0_1_counts.json e93826d4c535a41ea28155b837307a1a941f9ac9d205f8e8bf60421481e54f99

      Since 22e3f82b:

    • The run clone: /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics-c1, on branch trackb-b2-run-c1-20261016, cut from 892040c88a6a2342d5545b8379920e077e0f4609. Nothing else lives in it, so the scripts' clean-worktree check sees only the repository.

    • The runtime: /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2, the same as the first registration's, outside the clone, with Python 3.14.4.

      • Its distributions are the 164 pins in runtime-requirements-pins.txt (SHA-256 19e95ff7e9d6fc771e8a1b88285beae6ff10ad9e8e77fa24735d944caecec7f3).
      • policyengine-us 1.752.2 is among them, installed from its wheel.
      • populace-fit and populace-frame 0.1.0 are editable installs from /Users/maxghenis/PolicyEngine/_trackb-b2-run/populace-pinned. That checkout sits at the selectors' pinned head af02c917fcb3c50816bf3af9c2b64509e928889a, with fit tree 5c866378fdf5906b7a61da9977b8d028d1d36e9f and frame tree 7cfb9ee78beb74911963913f202a4471aae2f52b.
      • The floor builder and the scorer refuse any Python other than 3.14.4, and any version of numpy, pandas, scikit-learn, scipy, quantile-forest, populace-fit or populace-frame other than the pinned one. They also refuse any populace head, tree or unclean source other than the pins.
    • The operator's runtime check. The other pins are checked by the operator. Before each launch, runtime_check.py (SHA-256 348fc70c5600149140d9a3b27d2de44c9bcb52f988c36e5a73dd84a363ab7042) runs from outside the clone, with absolute paths:

      cd /Users/maxghenis/PolicyEngine/_trackb-b2-run && \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python -I \
        <evidence>/runtime_check.py <evidence>/runtime-requirements-pins.txt \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/populace-pinned
      
      • <evidence> is the first registration's folder on the operator's host; nothing is copied into the clone.
      • Its output is posted on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42, and must end RUNTIME OK.
      • At this registration it printed the same as at the first registration: executable /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python; pins_sha256 19e95ff7…; check_sha256 348fc70c…; distributions 166; listing_sha256 b774409c651ddfdd75eed26b35a1aad2e58ab1a2a07e94ff3b537350088d2986; populace_head af02c917…; RUNTIME OK.
    • The floor comparison. compare_floors.py (SHA-256 1af6d03cf1cfd6b1f2b95578afaec3053f0f6211b0c0c82d7429cd9a51745385) also runs from <evidence>/rescope/, under python3 -I, with the first floor's file and the rerun's.

      • It refuses a first floor whose SHA-256 is not 500f801e….
      • It checks each artifact's addendum_sha256 against the addendum at its own commit: f719edd9… for the first floor and 8896e8a7… for the rerun.
      • It requires the protocol block's hash to be equal in both.
      • It prints only booleans and key names, and must end TRUTH SIDE REPRODUCED.
    • policyengine-us 1.752.2, for the 2010 NAWI read only.

      • POPULACE_DYNAMICS_PE_US_DIR=/Users/maxghenis/PolicyEngine/_trackb-b2-run/pe-us-1.752.2 holds policyengine_us-1.752.2.dist-info/ and policyengine_us/parameters/gov/ssa/nawi.yaml, taken from the 1.752.2 wheel.
      • The file's hash matches the wheel's RECORD entry sha256=nbVf6eolRHoTl2fHcZsykgmC3SpliHyPwN2xKi3PfFM (SHA-256 hex 9db55fe9ea25447a139767c7719b32920982dd2a65887c8fc0ddb12a2dcf7c53). The operator checked this; the code does not.
      • The scorer refuses any other version, and any NAWI prefix that differs from the selectors' 2010 pins: 1,588 bytes, prefix SHA-256 e272a7bc…, mapping SHA-256 26b6a049….
    • Threads, jobs and workers.

      • Every process runs one BLAS, OpenMP and numexpr thread: OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS and NUMEXPR_NUM_THREADS are all 1.
      • It runs with OMP_WAIT_POLICY=PASSIVE and POPULACE_FIT_N_JOBS=4, and with the worker count set by the memory rule above. That stays within Max's 2026-10-11 cap for the shared host.
      • The floor builder and the scorer refuse any other thread setting, a POPULACE_FIT_N_JOBS outside 1 to 4, or more than four workers. They check this first, before reading anything. The comparator has none of these guards; its only check is that the floor artifact is committed.
      • Candidate 3's own frozen runner pinned eight threads and ACTIVE. B2 refits and projects candidate 3 at one OpenMP thread instead, through the selectors' _project, as protocol v2's planning run did. That run's record shows OMP_NUM_THREADS=1 and POPULACE_FIT_N_JOBS=4.
      • Why one thread. The first registration's account stands: on 2026-10-13 one multi-thread probe stalled at 0% CPU; on 2026-10-15 the same probe completed at 1, 2 and 4 threads (thread-hang-reproduction/summary.txt). The cause was not identified. B2 uses one thread because it completed in every run, and its cross-process determinism is tested. PR Track B B2: gate surface C1 (d1606) #545 brings b2.thread_settings' docstring into line with this account.
      • Forest seeds are per tree, so the forest job count is not expected to change a fit. B2 fixes it at 4 and does not rely on that expectation.
    • Determinism. tests/test_track_b_b2_threads.py was run at 22e3f82b in this runtime. Two fresh processes with different hash seeds gave:

      • an equal fitted state, field by field, leaving out only _BinMapper.n_threads;
      • equal gate probability surfaces;
      • byte-identical draws for all 20 seeds.

      PR Track B B2: gate surface C1 (d1606) #545 does not change the fit or the projection. The test was rerun at 892040c8 in this runtime: 2 passed (rescope/run/threads-determinism-892040c8-venv-b2.log).

    Procedure

    --registration-id is this comment's numeric GitHub comment id. Every #42 post below is a new comment. The operator is the registering session, named under "Exposure" below.

    1. Runtime check. The operator runs runtime_check.py as above and posts its output on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42.

    2. The floor builder runs once, from the clean run clone at 892040c88a6a2342d5545b8379920e077e0f4609:

      cd /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics-c1
      PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS='ignore::FutureWarning' PYTHONPATH=src:scripts \
        OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 VECLIB_MAXIMUM_THREADS=1 \
        NUMEXPR_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE POPULACE_FIT_N_JOBS=4 \
        POPULACE_DYNAMICS_PSID_DIR=/Users/maxghenis/PolicyEngine/psid-data \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python scripts/track_b_b2_floor.py \
        --registration-id <this comment's id> --expected-commit 892040c88a6a2342d5545b8379920e077e0f4609 --workers 4
      
      • It writes runs/track_b_b2_floor.json exclusively.
      • That file alone is committed, as the only change, in one commit whose parent is 892040c88a6a2342d5545b8379920e077e0f4609. The commit is pushed to trackb-b2-run-c1-20261016.
      • Then the operator runs compare_floors.py against the first floor and posts on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42: the commit id, the artifact's SHA-256 and the comparison's output.
      • The operator also posts:
        • the outcome;
        • the ladder;
        • the power;
        • undefined_share_ok for each gated cell (rule 7);
        • L, and the worker count set by the memory rule.

      When B2 stops here. B2 stops and returns to Max if any of these holds:

      • the floor records WEAK_POWER_OR_VACUITY;
      • the comparison does not end TRUTH SIDE REPRODUCED;
      • rule 7 or the memory rule stops B2.

      B2 then takes the floor-stop ending in step 5: no scorer is launched, and nothing is an attempt. The comparator still runs, because it gates nothing.

    3. The comparator runs once, at the floor commit and before the scorer, with its default ledgers and no --ledger:

      cd /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics-c1
      PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src:scripts \
        OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 VECLIB_MAXIMUM_THREADS=1 \
        NUMEXPR_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE POPULACE_FIT_N_JOBS=4 \
        /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python scripts/track_b_b2_compare_floor.py
      

      The comparator has no thread guard of its own, so its command sets the registered thread settings itself.

    4. The scorer runs once, only if step 2 reached none of its stops, from the clean run clone at the floor commit. The operator first checks, and posts, that:

      • the floor commit has a single parent, 892040c88a6a2342d5545b8379920e077e0f4609, and its only change is A runs/track_b_b2_floor.json;
      • the eight file hashes in the Runtime table hold at that commit;
      • runtime_check.py still ends RUNTIME OK.

      The launch.

      • The scorer gets the same environment as the floor builder, plus POPULACE_DYNAMICS_PE_US_DIR=/Users/maxghenis/PolicyEngine/_trackb-b2-run/pe-us-1.752.2.
      • Its arguments are the same --registration-id, --expected-commit <the floor commit>, and the worker count set by the memory rule.
      • --floor-artifact is not used, so the scorer reads the default runs/track_b_b2_floor.json.

      After the run.

      • Its output runs/track_b_b2_score.json and its attempt log are created exclusively, then committed and pushed to trackb-b2-run-c1-20261016.
      • After any candidate outcome there is no rerun, new seed, larger K, re-pruning, tolerance change or rescoping (addendum section 8).
    5. Publication and preservation. Exactly one of three endings applies.

      • A scored outcome. This needs a complete score file. The scorer writes runs/track_b_b2_score.json only after its attempt log records "full-support cells", and then logs wrote runs/track_b_b2_score.json (sha256 …). The file is complete when all three hold:

        • its SHA-256 equals the one that stage records;
        • it parses as JSON, with schema track_b_b2_score.v1 and an outcome among INVALID, FAIL, PASS and INCONCLUSIVE;
        • it holds all 16 cells.

        Then the outcome is published on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 whatever it is, with every gated and report-only cell scored, and the attempt goes on the cumulative ledger (design line 469).

      • A spent attempt without complete output. This covers three cases:

        • ABORTED_AFTER_GAPS;
        • a second interruption (see "Interruption");
        • a score file that exists but is not complete. The scorer writes that file only after "full-support cells", so such a file always falls here. That includes one that parses but whose digest the attempt log never recorded.

        When there is no score file. This ending applies only if the attempt log records "full-support cells", or if the interruption is the second. Two other cases keep their paths under "Interruption" and are not terminal: a refusal before the attempt log exists, and a first interruption before "full-support cells".

      • A floor stop. This is any stop in step 2, a floor build refused by one of its checks, or the memory rule's refusal above L = 4.

        • The status is published on Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42. With it go the floor artifact's commit and SHA-256 and the comparison's output, or, for a refusal that wrote no artifact, the refusal and its stderr. In that case the comparison and the comparator need the artifact, so they are skipped and recorded as unavailable.
        • No scorer runs and nothing is an attempt, so nothing goes on the cumulative ledger.
        • B2 returns to Max.

      In every ending, the run branch is then merged to master by a PR that adds only the run's files that exist: any floor artifact, score output, partial score bytes and attempt logs. The same PR adds the first floor as runs/track_b_b2_floor_v1_stopped.json, unchanged (SHA-256 500f801e…).

    Exposure of the registering session

    This registration was written by the session that drove Track B's B2 chain (Claude, Opus 5.5). The session is also the operator.

    What it has seen from 2012 and 2014 data, all truth-side and all before any candidate run.

    • Earlier, as before: the audit's frozen support counts for 2012 and 2014. These are presence counts.
    • Since the first floor, from the floor:
      • its outcome, ladder, power figures, guard flags and design summary;
      • which cells were eligible or at their caps;
      • the order of the cells by tolerance/σ;
      • each level cell's τ as a ratio to the nominal τ (0.866–0.989);
      • each option's tolerance width relative to k = 3.
    • How it priced the options. It computed power for d1342's options from the floor's tolerances, se_up and σ in scripts that printed only power, k, ratios and flags. It printed no tolerance, floor mean, SD, σ or truth moment, and read none directly.
    • The reviewers. Both GPT-6.1 Sol reviewers of the pricing reported that one of their diagnostic commands echoed tolerance fields from the committed floor into their sessions. Neither is an operator.

    The choice of C1. It was made with the floor in view, as d1342 provided: "the choice among B-D returns to Max on B2's real tolerances". No candidate output informed it.

    What it has not seen. No B2 candidate output, and no selector outcome at 2008 or 2010.

    B1's sealed output. B1's raw output and sealed logs, which hold reference-year 2014 values, stay unopened until B2's result is published.

    Ledger reads.

    • The session's first comparator run (step 3 of the first registration, comment 6200099988) read two fields of the boundary-2010 selection ledgers and printed only booleans.
    • Its second, in step 3 below, will read the same two fields the same way.
    • Both are recorded as its ledger reads.

    Review before posting

    Each round reviewed a cleaned copy of the repository at 892040c8, built by scripts/track_b_b0_1_review_root.py with the Q6 exclusion list removed. Its context held this text, the first registration, the rulings (d1606 included), the decision aid and its pricing files, the step-2 and pricing posts, PR #545's body, compare_floors.py, and the runtime and determinism evidence. Subfleet ran every round on Astra.

    • Round 1 requested changes: two medium findings and three low.
      • The mediums were the endings for stopped and aborted runs, and the comparator's thread settings.
      • The lows were three chronology wordings, the first floor's uncaptured exit code, and an addendum line range, plus stray backticks.
      • It verified every quoted hash that the supplied files allowed. In a copy without git history it could not verify the first floor's addendum hash, f719edd9….
      • It ran 44 in-memory checks of compare_floors.py, and found no truth-side field missing from it.
    • Round 2 requested changes: two medium findings and one low.
      • The mediums were a floor refusal that writes no artifact, and an incomplete score file.
      • The low was this review record.
      • It found every round-1 finding resolved, apart from those two gaps.
    • Round 3 requested changes: one medium finding and one low.
      • The medium: a missing score file is a spent attempt only after "full-support cells" or on a second interruption.
      • The low: this record overstated round 1's hash checks.
      • It confirmed that the completeness test matches the code, and that the artifact-less floor refusal is resolved.
    • Round 4 approved, with no findings. It confirmed that step 5's endings are mutually exclusive and complete, with every terminal and intermediate path assigned exactly once.

    No round met a blinded value.

  16. MaxGhenis commented on Oct 18, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2 (rescoped registration, comment 6252388925), step 1: runtime check.

    runtime_check.py (SHA-256 348fc70c…) was run from outside the clone, as the registration states. Its output:

    executable /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2/bin/python
    prefix /Users/maxghenis/PolicyEngine/_trackb-b2-run/.venv-b2
    python 3.14.4
    pins_file /Users/maxghenis/microcosm-launch-evidence/dynasim-parity-20260909/trackb-b2-registration-20261013/runtime-requirements-pins.txt
    pins_sha256 19e95ff7e9d6fc771e8a1b88285beae6ff10ad9e8e77fa24735d944caecec7f3
    check_sha256 348fc70c5600149140d9a3b27d2de44c9bcb52f988c36e5a73dd84a363ab7042
    distributions 166
    listing_sha256 b774409c651ddfdd75eed26b35a1aad2e58ab1a2a07e94ff3b537350088d2986
    populace_head af02c917fcb3c50816bf3af9c2b64509e928889a
    RUNTIME OK
    

    It ends RUNTIME OK. Every line matches the first registration's runtime check. The run clone /Users/maxghenis/PolicyEngine/_trackb-b2-run/microcosm-dynamics-c1 is on trackb-b2-run-c1-20261016 at 892040c88a6a2342d5545b8379920e077e0f4609, clean, with no untracked or ignored files. Step 2, the floor builder, follows.

  17. MaxGhenis commented on Oct 18, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2 (rescoped registration, comment 6252388925), step 2: the floor builder clears (LADDER_CLEARED).

    • How it ran. Once, from the clean run clone at 892040c8, with the registered command and environment: 4 workers, one BLAS/OpenMP/numexpr thread, OMP_WAIT_POLICY=PASSIVE, POPULACE_FIT_N_JOBS=4, --registration-id 6252388925.
      • It started at 16:38Z and wrote its artifact by 16:40Z.
      • Exit code 0. This time the code was captured, written to a file by the launching shell.
      • Its combined stdout and stderr hold only the stage lines: the read set under the read guard; the household-split floor, seeds 0-99; the person-split floor, seeds 0-99 (non-gating); the household bootstrap, 4,000 replicates; and the write.
    • Commit. 89c0ed9d6fe643469ada338cd5a2ae17059d194e on trackb-b2-run-c1-20261016, pushed. Its single parent is 892040c8, and its only change is A runs/track_b_b2_floor.json.
    • The artifact.
      • SHA-256 65c5fa52d03ce20497c38faa2c9dd78f25845efefef88d9790cbbffe59ca9ccd.
      • Fields: schema: track_b_b2_floor.v2, outcome: LADDER_CLEARED, candidate_runs_permitted: true, registration_id: 6252388925.
      • It opened 78 of the 80 pinned files, each with its pinned hash.

    Truth-side reproduction (rule 11). compare_floors.py (SHA-256 1af6d03c…) compared this artifact with the first floor (500f801e…). Its output:

    unexpected_key_differences []
    addendum_is_the_pinned_one first true
    addendum_is_the_pinned_one rescoped true
    equal protocol_v2_sha256 true
    equal planning_values_sha256 true
    equal counts_record_sha256 true
    equal config true
    equal counts true
    equal domain true
    equal design true
    equal geometry true
    equal person_split true
    equal psid_files_opened_sha256 true
    equal read_set_complete true
    equal psid_dir true
    equal cell_names true
    all_cell_fields_equal true
    top_level_fields_that_differ ['addendum_sha256', 'candidate_runs_permitted', 'code_sha256', 'ladder', 'outcome', 'power', 'registration_id', 'repository_head', 'schema', 'statements', 'surface', 'wall_seconds']
    TRUTH SIDE REPRODUCED
    

    Every truth-side field equals the first floor's. Only the fields the registration allows to differ do.

    Decision, inside GATE_SURFACE.

    • Design: 63 strata of two clusters, no merges, L = 1, ν = 63.
    • Eligibility: all four gate-surface cells are eligible.
    • Ladder: the order by descending tolerance/σ is earn_p90.prime, earn_zero_rate.prime, earn_zero_rate.older, earn_p90.older. Power is 0.9226 before any pruning, so the ladder stops at once. Nothing is pruned.
    • Guards: at_least_four_cells: true and not_every_tolerance_at_cap: true. No gated tolerance is at its cap.
    • Gated: earn_p90.older, earn_p90.prime, earn_zero_rate.older, earn_zero_rate.prime. That is m = 4, with t*₄,₆₃ = 2.5714.
    • Headline: 4 gated cells. One (earn_zero_rate.older) is labelled "published candidate-3-law outcomes at boundaries 2008 and 2010", and three are labelled "published five-seed floor detail, no published candidate outcome".
    • Report-only reasons: the medians are "outside the rescoped surface (d1606 option C1)", and the other ten are "outside the ruled surface (d781 (a))".

    Rule 7. cells[c].se_up.undefined_share_ok is true for all four gated cells.

    Memory rule. L = 1, so the scorer runs with 4 workers.

    None of step 2's stops applies. Steps 3 (the comparator) and 4 (the scorer) follow.

    Blindness: no 2012 or 2014 floor value, tolerance or truth moment is quoted here. They are in the committed artifact. No candidate outcome exists.

  18. MaxGhenis commented on Oct 18, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2 (rescoped registration, comment 6252388925), steps 3 and 4: the comparator, and the scorer's pre-launch checks.

    Step 3: the comparator

    • How it ran. Once, at the floor commit 89c0ed9d, with its default ledgers and no --ledger.
      • The registered thread settings were all in its command: the five thread variables at 1, OMP_WAIT_POLICY=PASSIVE, and POPULACE_FIT_N_JOBS=4.
      • Exit 0, with empty stderr.
    • Output. Four booleans, all true: the six-cell floor block and the seed detail, for each of docs/analysis/m6_qstar_train_only_selection_results.json and docs/analysis/m6_rhostar_train_only_selection_results.json, in that order.
    • This gates nothing. It is recorded as the operator's second ledger read, of the same two fields as the first.

    Step 4: the scorer's checks before launch

    parents: 892040c88a6a2342d5545b8379920e077e0f4609 
    changes: A runs/track_b_b2_floor.json
    head: 89c0ed9d6fe643469ada338cd5a2ae17059d194e
    clean: yes
    src/populace_dynamics/track_b/b2.py ok
    scripts/track_b_b2_floor.py ok
    scripts/track_b_b2_compare_floor.py ok
    scripts/track_b_b2_score.py ok
    scripts/track_b_b0_1_planning_values.py ok
    scripts/track_b_b0_1_ruled_power.py ok
    docs/design/track_b_b0_1_planning_values.json ok
    docs/design/track_b_b0_1_counts.json ok
    eight hashes hold: True
    runtime exit 0
    
    • The run clone is at 89c0ed9d and clean. The floor commit's single parent is 892040c8, and its only change is A runs/track_b_b2_floor.json.
    • The eight file hashes in the registration's Runtime table hold at that commit.
    • runtime_check.py ends RUNTIME OK, identical to step 1.
    • NAWI. POPULACE_DYNAMICS_PE_US_DIR holds the 1.752.2 nawi.yaml with its RECORD hash 9db55fe9….

    The scorer is launched now. It runs with the same environment as the floor builder, plus POPULACE_DYNAMICS_PE_US_DIR=/Users/maxghenis/PolicyEngine/_trackb-b2-run/pe-us-1.752.2, and with --registration-id 6252388925 --expected-commit 89c0ed9d6fe643469ada338cd5a2ae17059d194e --workers 4, with no --floor-artifact. The memory rule gives 4 workers, since L = 1.

  19. added a commit that references this issue on Oct 18, 2026
  20. MaxGhenis commented on Oct 18, 2026

    @MaxGhenis
    ContributorAuthor

    Track B B2 (rescoped registration, comment 6252388925), step 5: the outcome is FAIL.

    The run

    • How it ran. The scorer ran once, from the clean run clone at the floor commit 89c0ed9d, with the registered command and environment and --workers 4. It started at 16:41Z and exited 0 at 17:11Z, after 1,744 s.
    • Its stages:
      1. the read set under the read guard;
      2. the fit of candidate 3's law at origin 2010, with seed 5200, 321,500 input rows, and inputs through 2010;
      3. the projection of the 20 draws (seeds 6200-6219) to 2012 and 2014;
      4. the full-support cells;
      5. the design bootstrap, 4,000 replicates, with no replicate failures;
      6. side A of gate seeds 0-4;
      7. the write.
    • Commit. 430f8883699609278ccfc425fd9563993b654888 on trackb-b2-run-c1-20261016, pushed. Its single parent is the floor commit 89c0ed9d. It adds only these two files:
      • runs/track_b_b2_score.json, SHA-256 df82a102a2b4c40c34f0e36b4333dbe7b90d13bfb3658b60b3f6d3f3e2dd8489;
      • runs/track_b_b2_score.attempt.jsonl, SHA-256 bb77de73413a52252b124c64e49a230202efd8ba9502922f3b8570d555e0dab5.
    • The score file is complete under step 5's test, so this is the scored ending:
      • its SHA-256 equals the one the attempt log's wrote runs/track_b_b2_score.json (sha256 df82a102…) stage records;
      • it has schema: track_b_b2_score.v1 and outcome: FAIL;
      • it holds all 16 cells.

    Why it fails

    The outcome is the first rule of element 9 that applies:

    1. INVALID: no. No gated cell is invalid, on full support or on any gate seed's side A.
    2. FAIL: yes, on both counts.
      • Some |g_c| > tol_c. For earn_zero_rate.prime, |g| = 0.3034 against a tolerance of 0.202. The metric is the log ratio of the projected rate to the realized one, so the projected prime-age zero-earnings rate is about 35% above the realized rate.
      • Fewer than four of five gate seeds pass. None does: a seed passes only when every gated cell's side-A gap is within tolerance, and earn_zero_rate.prime's is within on none of the five.

    Three of the four gated bounds also exceed their tolerances; only earn_p90.prime passes the bound rule.

    The cells

    Gated cells (m = 4, t*₄,₆₃ = 2.5714; every Monte Carlo and undefined-share rule holds):

    Cell Gap g Tolerance |g| within se_c Bound |g| + t*·se Bound within Side A seeds within Monte Carlo rule
    earn_p90.older 0.1213 0.140 yes 0.0259 0.1878 no 4/5 yes
    earn_p90.prime 0.0371 0.106 yes 0.0209 0.0909 yes 5/5 yes
    earn_zero_rate.older 0.0774 0.146 yes 0.0278 0.1489 no 5/5 yes
    earn_zero_rate.prime 0.3034 0.202 no 0.0414 0.4099 no 0/5 yes
    Report-only cell Metric Gap g Tolerance |g| within se_c Unadjusted 95% interval
    earn_autocorr_lag1 abs_gap_corr -0.0017 0.047 yes 0.0167 [-0.0349, 0.0316]
    earn_autocorr_lag2 abs_gap_corr 0.0948 0.083 no 0.0249 [0.0450, 0.1446]
    earn_dlog_mean.older abs_gap_log 0.0739 0.049 no 0.0140 [0.0460, 0.1018]
    earn_dlog_mean.prime abs_gap_log 0.0076 0.047 yes 0.0121 [-0.0165, 0.0317]
    earn_dlog_sd.older log_ratio 0.0570 0.191 yes 0.0600 [-0.0629, 0.1769]
    earn_dlog_sd.prime log_ratio -0.0276 0.181 yes 0.0665 [-0.1605, 0.1052]
    earn_mob_h1_diag log_ratio -0.0580 0.041 no 0.0147 [-0.0875, -0.0286]
    earn_mob_h2_diag log_ratio 0.0459 0.079 yes 0.0228 [0.0003, 0.0916]
    earn_p10.older log_ratio 0.0437 0.249 yes 0.0756 [-0.1074, 0.1948]
    earn_p10.prime log_ratio 0.0102 0.189 yes 0.0742 [-0.1381, 0.1586]
    earn_p50.older log_ratio 0.1078 0.106 no 0.0221 [0.0637, 0.1520]
    earn_p50.prime log_ratio 0.0635 0.080 yes 0.0185 [0.0265, 0.1006]
    • Gaps. Log-ratio cells' gaps are log(projected / realized). The abs_gap cells' gaps are projected minus realized.
    • Tolerances. These are the floor's, in the metric's units. Plain-unit statements of each, and the realized and projected values, are in the committed artifact.
    • Report-only cells are outside the Bonferroni family and decide nothing.

    What follows

    • B2 is not admitted (GATE_FAIL_OR_REGRESSION_FAIL, design §7). Its admission and every dependent milestone are blocked: H and B3L cannot rely on B2.
    • No relief. The failure is published with no seed selection and no tolerance relief. Under addendum section 8 there is no rerun, new seed, larger K, re-pruning, tolerance change or rescoping. Any successor needs a new registration.
    • The attempt ledger. This is B2's first scored attempt of candidate 3's law, and it goes on the cumulative attempt ledger (design §7, "Attempt ledger"). Two independently justified registered successors that fail would trigger SUCCESSOR_EXHAUSTED. The first registration's floor build was not an attempt.
    • The results PR. One PR will add the run's files to master:
      • the rescoped floor, the score and the attempt log;
      • the first floor, as runs/track_b_b2_floor_v1_stopped.json (500f801e…), unchanged;
      • the ledger's first entry, in docs/design/track_b_attempt_ledger.md.
    • B1's sealed output may now be opened. It is not opened by this post.
    • What happens next for the earnings lane goes to Max.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions