Skip to content

M6 design amendment 2: forward rank-to-level + re-ranking laws (§2.7.6, closes Sol round-2 blocker)#178

Merged
MaxGhenis merged 2 commits into
masterfrom
m6-forward-earnings
Jul 13, 2026
Merged

M6 design amendment 2: forward rank-to-level + re-ranking laws (§2.7.6, closes Sol round-2 blocker)#178
MaxGhenis merged 2 commits into
masterfrom
m6-forward-earnings

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

M6 design amendment 2 — forward rank-to-level + re-ranking laws (§2.7.6)

Adds §2.7.6 to docs/design/m6_projection_engine.md (design doc revision 3),
closing the engine build's round-2 blocker. Docs-only, DRAFT; no gates.yaml edit,
no floor, no test.

Why (Sol's round-2 blocker)

~/PolicyEngine/sol-worktrees/m6-forward-REPORT.md: §2.7 pinned the forward
conditional-rank draw and the real per-year seam, but never specified the
leakage-safe map from a drawn rank to a positive earnings level for 2016/2018,
nor its inverse (recovering the drawn-2016 rank at the 2018 step). The certified
de-index (run_gate1_candidate7.py:648-654) maps the drawn rank through a
(age_bin, period) CellMarginal quantile — and a ≤2014 fit has no
(age_bin, 2016) cell, which §2.7.1 forbids inventing. Sol correctly stopped
rather than choose one of several materially different laws.

What §2.7.6 pins (zero design choices left)

  1. Rank-to-level law — PIN (b2). A NAWI-normalized calendar-invariant ≤2014
    age_bin CellMarginal (the certified fit_cell_marginals machinery verbatim,
    only re-keyed (age_bin,period)→age_bin on normalized levels); de-index a drawn
    rank through its Qhat_pos quantile, then re-index to the target year by a
    ≤2014-projected wage index. Rejected: (a) donor-level bootstrap (the
    certified de-index is a marginal quantile, not a donor-level carry —
    _knn_draw returns the donor's rank; an R2 departure with no clean inverse);
    (b1) carrying the 2014 period marginal (§2.7.1-prohibited, biases earn_p10
    low).
  2. Wage index I_proj + leakage prohibition. I_proj(2016/2018) = OLS
    ln(NAWI)~year over the trailing decade [2005,2014], extrapolated. Using
    realized post-T* NAWI on the scored path is PROHIBITED — it would leak
    realized wage growth into the gated level cells. Concretely, the frame's realized
    nawi column (the aging step) is fenced off the scored de-index and admissible
    only for the non-scored seam / taxable_max and the report-only alignment
    path
    (decision 9, §4.8; the M2 calibration_disclosure lesson — the gated path
    is the un-aligned projection).
  3. Re-ranking law — PIN the fit-population CDF (rhat, the exact inverse of
    the rank-to-level map, so rank→level→rank round-trips). Rejected: within-frame
    percentile (references the synthetic projected cohort, not the PSID fit
    population — an R2 violation that breaks the round-trip). R2: the certified
    chain's ranks are CellMarginal CDF positions, so the deployed re-rank must
    reference the fit-population marginal.
  4. Four secondary byte-determinism gaps: memory-after-2018 rolling
    generated-lag columns (gen_earn_w2/w4); the seed_from_generator → SeedSequence([seed, code]) substream bridge (the existing m6-engine helper +
    SUBSTREAM_CODES); the concrete frame schema (u_w, realized_earn_2014/2012,
    the generated-lag columns; materialized by the ≤2014 refit); and age-bin
    clipping to the fitted [25,59] range.

Lock dependency

§2.7.6.6 states plainly that this closes Sol's round-2 blocker and that the
gate_m6 lock flip's design_commit must finalize to this amendment's merge
commit
(not the round-2 d6abb16) — the gate_m6 lock is holding for it.

Not in scope

No gates.yaml edit, no floor, no test, no code. Sol implements the forward
generator against §2.7 + §2.7.6 next.

🤖 Generated with Claude Code

Close Sol's round-2 blocker (~/PolicyEngine/sol-worktrees/m6-forward-REPORT.md):
§2.7 specified the forward conditional-RANK draw but not the leakage-safe map
from a drawn rank to a positive earnings LEVEL for 2016/2018 (the certified
de-index run_gate1_candidate7.py:648-654 uses (age_bin, period) marginals that do
not exist post-2014), nor its inverse. §2.7.6 pins both with adjudication-grade
option analysis, plus the four secondary byte-determinism gaps, so the engine lane
implements with zero design choices:

- rank-to-level: PIN (b2) a NAWI-normalized calendar-invariant <=2014 age_bin
  CellMarginal (certified fit_cell_marginals verbatim, key (age_bin,period)->
  age_bin), de-indexed then re-indexed by a <=2014-projected wage index; reject
  (a) donor-level bootstrap (departs from the certified marginal-quantile
  de-index, an R2 violation, no clean inverse) and (b1) carrying the 2014 period
  marginal (§2.7.1-prohibited, biases earn_p10 low).
- wage index I_proj: OLS ln(NAWI)~year over the trailing decade [2005,2014],
  extrapolated to 2016/2018; realized post-2014 NAWI on the SCORED path is
  PROHIBITED -- concretely the frame's realized nawi column (steps.py aging) is
  fenced off the gated de-index and admissible only for non-scored
  seam/taxable_max and the report-only alignment path (decision 9, M2
  calibration_disclosure; gated path = un-aligned projection).
- re-ranking: PIN the fit-population marginal CDF (rhat, the EXACT inverse of the
  rank-to-level map); reject within-frame percentile (references the synthetic
  projected cohort, an R2 violation, breaks the round-trip).
- secondary gaps: memory-after-2018 rolling generated-lag columns
  (gen_earn_w2/w4); the seed_from_generator -> SeedSequence([seed,code]) substream
  bridge; the concrete frame schema; and age-bin clipping to the fitted [25,59].

States the lock dependency: the gate_m6 lock flip's design_commit must finalize to
this amendment's merge. Docs-only; no gates.yaml edit, no floor, no test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
social-security-model Ready Ready Preview, Comment Jul 13, 2026 5:01pm

Request Review

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Adversarial verification — design amendment 2 (§2.7.6, 4ee9a7d)

Verdict: UNRESOLVED / MAJOR (1 MAJOR, 3 MINOR). The rank↔level and re-ranking laws, the leakage fence, the RNG bridge, and the secondary byte-determinism pins are almost all code-true and R2-defensible — the transfer machinery checks out and the exact-inverse claim holds on the grid interior. But item 5 (age support) fails, exactly where the brief flagged the highest risk: the pinned [25,59] clip is incoherent with the committed gated cell registry, whose "older" earnings cells span ages 45–64. The doc's justifying claim that "the gated earnings-moment surface is the 25–59 range" is factually false, so the forward law as pinned distorts two gated cells and cannot faithfully produce the scored surface. That blocks the amendment's "zero design choices" close until reconciled.

Item 5 (highest-risk) — explicit result: MISMATCH CONFIRMED, MAJOR.

The gated earnings cells are stratified prime/older by EARN_COHORTS = (("prime", 25, 44), ("older", 45, 64)) (scripts/build_m6_holdout_floors.py:120), and earnings_frame applies them with no [25,59] pre-filtercohort_of keeps ages 25–64 (:371-378); family.family_earnings_panel itself has no age cap (only earnings < _MISSING & weight > 0). The v3 builder reuses these earnings cells verbatim via importlib (build_m6_holdout_floors_v3.py:80-84), and the block draft gates earn_zero_rate.older and earn_dlog_sd.older (v3 tolerances 0.163 / 0.269). So the "older" gated cells read realized PSID earnings for ages 45–64, including 60–64. But the forward law is fit on [25,59] (origin/m6-engine:engine/refit.py:802-806) and §2.7.6.5 clips >59 → 55-59, de-indexing ages 60–64 through the 55–59 marginal. §2.7.6.5's premise — "the gated earnings-moment surface (§4.5) is the 25-59 range … <25 and 60+ draws are non-scored plumbing" — is contradicted by the very floors the block draft gates. earn_zero_rate.older is worst: the 60–64 zero/retirement transition (the cell's whole signal) is projected from a 55–59 marginal that understates it. Fix required: re-scope the gated "older" cell to [45,59] in the floors (re-opens the floors ceremony), or extend the forward fit to 64 (no certified earnings object covers 60–64 — gate-1 age_range is [25,59]), or acknowledge the 60–64 clip distortion and demote earn_*.older to report-only. None is done; the doc asserts a 25–59 gated surface that does not exist.

Findings

1 — MAJOR (item 5). Age-support pin incoherent with the gated cell registry. §2.7.6.5's [25,59] clip + "gated surface is 25-59" claim contradict EARN_COHORTS older = 45-64 (build_m6_holdout_floors.py:120, reused by v3), which gates earn_zero_rate.older / earn_dlog_sd.older on ages including 60–64 outside the fit range. The law clips 60–64 to the 55–59 marginal, systematically distorting those two gated cells. See the explicit result above.

2 — MINOR (item 2, exact-inverse round-trip). The "round-trips exactly" claim is true only on the strictly-monotone interior; ties and tail-clamps are unqualified. CellMarginal.quantile(u)=np.interp(u,wtil,yval) clamped [ymin,ymax] and rank(y)=np.interp(y,yval,wtil) clamped [0.001,0.999] (candidate5b:250-259) are algebraic inverses iff yval is strictly increasing and u∈[wtil[0],wtil[-1]]. On tied earnings (flat yval), np.interp(y,yval,wtil) returns the first matching wtil — deterministic but not the original u, so the 2018 re-rank recovers a shifted rank_t; on tail draws it clamps (the certified code itself tracks these as corner_bottom/corner_top, candidate7:654-656). The zero→p0/Q0 path is handled consistently. Impact is small (rare exact ties in NAWI-normalized earnings; few tail persons) and byte-deterministic, but the doc should qualify "exact on the strictly-monotone interior; ties/tail-clamps deterministic-but-inexact."

3 — MINOR (item 3, I_proj derivability + honesty). I_proj = OLS ln(NAWI)~year over [2005,2014] on ss/params.py NAWI (nawi.yaml, :226; base NAWI(1977)=9779.44 fixed, :334). This is leakage-safe in the sense that matters: SSA NAWI is final on publication (never revised — ss/params.py:342 notes only the forward years are Trustees projections), so ≤2014-year values are clean ≤T* quantities and the window cannot be holdout-tuned. Two honesty nits: (a) NAWI_2013/2014 were published in 2015/2016 (2-yr lag), so "≤2014-derivable" should be stated as "uses NAWI values for years ≤2014" (final/unrevised), not "available by end-2014"; (b) "the same trailing-window logic the certified indexing convention uses" is a loose analogy — AIME wage-indexing (index to the age-60 AWI) is not a trailing-decade OLS. The exact decade is one of several ≤2014-admissible windows (pinned, so no engine-lane freedom, but not uniquely forced).

4 — PASS (item 4, leakage fence). The only realized-macro columns are written by the aging step (nawi_by_year→nawi, wage_base_by_year→taxable_max, steps.py:145-156); no engine module reads them into a scored earnings moment (grep of engine/*.py: sole writers, no scored readers). §2.7.6.5's frame schema explicitly excludes nawi from the generator's read set, the gated path is the un-aligned projection (decision 9), and the alignment/taxable_max/AIME/claiming paths that touch realized NAWI are report-only. No unfenced path to a gated cell found (a design commitment the un-implemented generator must honor via the pinned schema).

5 — PASS (item 1 rejections + item 6 pins). The certified de-index is exactly cell.quantile(u_prev) (candidate7:648-654, a CellMarginal quantile — not donor-level), so rejecting (a) donor-level as a mechanism change with no clean inverse is code-grounded, and rejecting (b1) carry-2014-marginal as §2.7.1-prohibited is correct. The RNG bridge is code-true: engine.rng.seed_from_generator exists and is int(generator.integers(0, np.iinfo(np.uint64).max, dtype=np.uint64)) (engine/rng.py:172,180) = the doc's 2**64-1; SUBSTREAM_CODES and the SeedSequence([seed,code]) construction match (candidate10:239, candidate10:242-252). Memory-after-2018 (gen_earn_w2/gen_earn_w4), the frame schema, and carry-forward are fully pinned. Nit (item 1 precision): "This keeps the certified de-index object" overstates — (b2) re-fits a new age_bin-keyed CellMarginal on NAWI-normalized pooled ≤2014 earnings (a new estimated object with a calendar-invariance assumption, first-certified by gate_m6); it keeps the certified mechanism (CellMarginal quantile), which is the R2-relevant property. Reword object→mechanism.

Read-only adversarial verification; no repo edits, no merge/ready. Verified against 4ee9a7d, origin/m6-engine, the committed v3 floors builder, and the certified candidate-5b/7 machinery.

…; NAWI lag

Verifier UNRESOLVED/MAJOR (comment 4960445225):

- MAJOR: the gated older earnings cohort spans 45-64 (EARN_COHORTS
  build_m6_holdout_floors.py:120; no panel age cap), but §2.7.6.5 clipped the
  law to the gate-1-transferred [25,59], so 60-64 would de-index through the
  55-59 marginal and kill earn_zero_rate.older. Per adjudication (option ii),
  EXTEND the fit to [25,64]: the forward participation-gate, donor pools,
  memory ramp, and NAWI-normalized marginal all fit [25,64] pooled <=2014
  (which contains 60-64 + the retirement-transition signal); age_bin grid ->
  8 bins {25-29..60-64}; clip <25->25-29, >64->60-64; <25 & 65+ non-scored.
  State the principle: the law must cover the surface it is scored on;
  narrowing the surface to fit model support is candidate-informed surface
  design, prohibited. Re-verified every gated earnings cohort span in [25,64].
- MINOR 1: qualify the round-trip -- exact on the strictly-monotone interior;
  corner clamps ([ymin,ymax]/[0.001,0.999]) and flat-yval ties are
  deterministic-but-inexact (candidate5b:250-259, corner_bottom/top); the 2018
  re-rank is deterministic so chain integrity holds (bounded corner rank
  perturbation).
- MINOR 2: disclose the NAWI publication lag (NAWI_2013/2014 published post-2014
  but a final/unrevised series, so leakage-safe -- date, not information, is
  post-T*); drop the loose "same as certified indexing" analogy.
- MINOR 3: "keeps the certified MECHANISM" (re-fits a new age_bin normalized
  object through the same CellMarginal machinery), not "object".

Docs-only; no gates.yaml edit, no floor, no test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Revision — verifier UNRESOLVED/MAJOR addressed (commit 722f59b)

All four findings from the verifier (comment 4960445225) are addressed in §2.7.6.
Docs-only; no gates.yaml edit, no floor, no test.

MAJOR — the fit range must cover the scored surface (extend to [25,64])

The verifier is right: the gated earnings cohorts are prime = 25–44 and
older = 45–64 (EARN_COHORTS, scripts/build_m6_holdout_floors.py:120; the panel
has no age cap), so earn_*.older scores ages to 64 — my [25,59] clip
would de-index 60–64 through the 55–59 marginal and kill the
earn_zero_rate.older signal. Per the adjudication (option ii), §2.7.6.5 now
extends the fit: the forward participation gate, donor pools, memory ramp, and the
NAWI-normalized marginal all fit [25,64] pooled ≤2014
(the ≤2014 PSID panel
contains 60–64 and exactly the retirement-transition signal the older cells score,
so the extension is ≤2014-derivable). The age_bin grid extends to 8 bins
{25–29, 30–34, 35–39, 40–44, 45–49, 50–54, 55–59, 60–64}; the clip moves to <25 → 25–29, >64 → 60–64; <25/65+ stay non-scored plumbing. The doc states the
principle — the law must cover the surface it is scored on, and narrowing the
surface (re-scope or demote a gated cell) to fit the model's support would be
candidate-informed surface design, prohibited in that direction — and frames
[25,59] as a non-transferable hyperparameter (the transfer conflicts with the
pre-registered truth-side surface). Re-verified: every gated earnings cohort span
(prime 25–44, older 45–64) lies inside [25,64], cited to
build_m6_holdout_floors.py:120.

MINOR 1 — round-trip claim qualified (§2.7.6.4)

Qhat_pos/rhat are np.interp on one (wtil, yval) grid, so the inverse is
exact on the strictly-monotone interior; at the corners it is deterministic
but inexact
quantile clamps to [ymin, ymax], rank to [0.001, 0.999], and
a flat-yval tie maps to the first wtil (candidate5b:250-259; tracked as
corner_bottom/corner_top). Consequence stated for the 2018 re-rank: rank(ℓ) is
a pure deterministic function of the carried level, so chain integrity holds; the
inexactness is only a bounded rank perturbation at the corners, not a break in
reproducibility.

MINOR 2 — NAWI publication lag disclosed (§2.7.6.3)

Added: NAWI_2013/NAWI_2014 are published ~2 years later (post-2014), but the
AWI is a final, unrevised series, so a NAWI_y with y ≤ 2014 is a fixed
function of ≤2014 wages — leakage-safe; only the publication date, not the
information, is post-T*. Dropped the loose "same logic as the certified indexing
convention" analogy for the plain statement.

MINOR 3 — "mechanism" not "object" (§2.7.6.2)

Now reads "keeps the certified de-index MECHANISM — it re-fits a new age_bin
NAWI-normalized object through the same CellMarginal machinery, not the
certified (age_bin, period) object itself."

Verification

JSON ledger revalidated; the only remaining 25–59 mentions are the intentional
"transferred-but-extended" framing in the corrected age-support bullet and the
ledger. CI green (below).

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Verification round (round-diff) — amendment-2 revision (722f59b)

Verdict: VERIFIED (ready to merge). All four findings are resolved in substance, not renamed, and the extend-to-[25,64] fix is backed by real ≤2014 data — the 60–64 bin the extension now fits is well-supported, so it introduces no new thin-support problem. The diff is docs-only (+59/−24, m6_projection_engine.md only); the JSON ledger revalidates (parses, revision: 3, re_ranking_law + age_support fields updated); the remaining 25–59 mentions are only the intentional non-transferable-hyperparameter framing.

The 60–64 support check (the load-bearing new question) — STRONG PASS

I quantified the ≤2014 PSID family_earnings_panel directly (POPULACE_DYNAMICS_PSID_DIR=~/PolicyEngine/psid-data):

stratum (≤2014) obs persons positive-earnings obs zero share
prime 25–44 162,955 22,358 137,732 0.155
older 45–64 108,038 13,680 78,980 0.269
60–64 (the new bin) 20,941 6,504 11,779 (~3,905 in the biennial 1998–2014 waves) 0.438
55–59 (ref) 25,058 8,020 17,647 0.296

Forward biennial donor pairs at 60–64 (≤2014): 5,352 — far above the k-NN k=25. So the 60–64 CellMarginal (plotting-position grid on ~3.9k positive obs) and donor pools are amply supported; the extension is genuinely ≤2014-derivable, not a thin bin. And the 60–64 zero share 0.438 vs 55–59's 0.296 confirms the retirement-transition zero-inflation earn_zero_rate.older scores is present and distinct at 60–64 — i.e. the old [25,59] clip really would have understated it by ~14pp (the exact signal-kill I flagged), and the fix captures it.

Per-finding resolution

MAJOR (fit range) — RESOLVED, code- and data-grounded. §2.7.6.5 now extends the forward participation gate, donor pools, memory ramp, and the NAWI-normalized marginal all to [25,64] pooled ≤2014; the age_bin grid goes to 8 bins {25–29 … 60–64} (certified was N_AGE_BINS = 7, candidate5b:155, which clip((age−25)//5, 0, 6) sends 60+ → 55–59 — this is exactly the distortion I flagged, so the certified code corroborates both the problem and the fix); clip moves to <25 → 25–29, >64 → 60–64. Both gated cohorts — prime 25–44, older 45–64 — are verified inside [25,64], cited to build_m6_holdout_floors.py:120. §2.7.6.2 also extends the marginal to the [25,64] grid and no longer claims the 55–59-clipped surface. The direction-of-fit principle is stated correctly: the law must cover the pre-registered truth-side surface, and narrowing it (re-scope/demote a gated cell) to fit the model's support would be candidate-informed surface design, prohibited in that direction; [25,59] is framed as a non-transferable hyperparameter. Resolution, not renaming.

MINOR 1 (round-trip) — RESOLVED. §2.7.6.4 now states interior-exact + corner/tie deterministic-but-inexact (quantile clamps [ymin,ymax], rank clamps [0.001,0.999], flat-yval tie → first wtil, candidate5b:250-259, tracked corner_bottom/top), with the consequence pinned: rank(ℓ) is a pure deterministic function of the carried level so chain integrity holds, the inexactness is only a bounded corner rank-perturbation, not a reproducibility break. The re_ranking_law JSON field is updated to match. Exactly the qualification I asked for.

MINOR 2 (NAWI publication lag) — RESOLVED, reasoning sound. §2.7.6.3 discloses that NAWI_2013/2014 are published ~2 years later, but the SSA AWI is a final, unrevised series (SSA does not restate prior AWI once set — a NAWI_y, y ≤ 2014, is a fixed function of ≤2014 wages), so only the publication date is post-T*, not the information — leakage-safe. The AWI-is-final claim is correct (AWI values are locked for benefit computation and not restated). The loose "same logic as the certified indexing convention" analogy is dropped.

MINOR 3 (mechanism vs object) — RESOLVED. §2.7.6.2 now reads "keeps the certified de-index MECHANISM — it re-fits a new age_bin NAWI-normalized object through the same CellMarginal machinery, not the certified (age_bin, period) object itself."

Read-only round-diff verification; no repo edits, no merge/ready. Verified against 722f59b, the certified candidate-5b machinery, and the live ≤2014 PSID panel.

@MaxGhenis MaxGhenis marked this pull request as ready for review July 13, 2026 17:10
@MaxGhenis MaxGhenis merged commit 4c6a0f6 into master Jul 13, 2026
7 checks passed
MaxGhenis added a commit that referenced this pull request Jul 13, 2026
…ash-merge

Final re-finalization. §2.7.6 (PR #178, squash-merge 4c6a0f6, twice-verified --
VERIFIED comment 4960583620) COMPLETED the forward rank-to-level + re-ranking
laws -- the forward earnings law the gate_m6 `covers` references. Per the block
draft's own design_commit_note (finalize the pin to the squash-merge at the lock
flip), the live design_commit is RE-FINALIZED from the interim #175 squash
(ce9893b) to the #178 squash (4c6a0f6), so the lock references the COMPLETE law.

Merges origin/master (#178, the §2.7.6 design-doc completion) and regenerates
gates.gate_m6 from the (unchanged) draft VERBATIM with the four lock-time deltas
(locked / status / +history / design_commit). The history
design_commit_finalized record now carries from d6abb16 -> to 4c6a0f6 with
refinalized_from ce9893b and the §2.7.6 reason; the content narrates the
two-step finalization.

Binding updates (tests/test_gate_m6_derivations.py): design_commit 4c6a0f6, the
design_commit_finalized.refinalized_from + the §2.7.6 reason; the design-commit
mutation now drifts the stale interim ce9893b. tests/test_gate_m6_floors.py
asserts live design_commit == 4c6a0f6 (draft d6abb16 unchanged).

runs/legacy_manifest_v1.json re-recorded via build_legacy_manifest.py
--transition (contract_revision + the gates.yaml entry only). Tier counts
unchanged (artifact 944, total 2,684). Sibling gates byte-identical to
origin/master (4c6a0f6); gate_m6 the sole new key. Both design dependencies now
satisfied: #175 (forward-law margin + re-pin) and #178 (§2.7.6 forward-law
completion).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Jul 13, 2026
…177)

* gate_m6 lock flip: temporal-holdout gate added locked (ratified 1818784)

Adds gates.gate_m6 as a NEW LOCKED top-level gate (the gate_w1 / gate_m4
pattern -- added locked, NOT an in-place unlocked->locked flip). The block is
docs/design/gate_m6_block_draft.yaml (the ratified v3 draft) carried VERBATIM
with exactly three lock-time deltas: `locked: true`, `status: locked`, and a
`history` ceremony entry. Every sibling gate (gate_1 / gate_2 [2a + 2b + 2c] /
gate_3 / gate_m4 / gate_w1) is untouched; gate_m6 is the sole new key.

Ceremony (gate_m6.history entry 2026-07-13-m6-gate-lock): the v1
OC-before-lock floor fired the PAUSE honestly (1 gated flow cell, combined
p_gate 0.8449 -- a both-directions failure); the candidate-blind pinned-ladder
redesign (coarsening + earnings decompounding, truth-side power arithmetic
only) was adjudicated on the public record; v2 cleared 0.9067; the adversarial
referee AMEND x4 (comment 4958425437) found the marital-ladder enumeration
incomplete (the asymmetric age-2 rung where remarriage.18-64 clears at 0.403);
fixes + the orchestrator's adjudicated v3 (comment 4958956779) landed 11 gated
cells (5 flow + 6 earnings), p_seed 0.8934 / p_gate 0.9087; re-check PROCEED TO
LOCK (comment 4959310892); ratified 2026-07-13 by merge of PR #172 (squash
1818784) under the maintainer's standing campaign directive of 2026-07-07. The
headline not_certified declaration names mortality drift FIRST; the SSA/NCHS
report-only mortality anchor is required before any M7-seeding claim; ZERO
mortality / widowhood cells are gated on verified power grounds.

ZERO threshold movement vs the frozen floor: the flip READS
runs/m6_holdout_floors_v3.json (sha256 e931c886...) and rewrites nothing.

Bindings go LOCKED-HOT: tests/test_gate_m6_derivations.py binds the live
gates.yaml gate_m6 block to the v3 floor (11-cell registry equality, every
tolerance recompute round(mean + k*sd, 3) capped, OC recompute 0.8934 / 0.9087
from the realized sigmas, not_certified ordering [mortality first], year pins /
shock-window exclusions bound to design_pins, v1/v2/v3 lineage shas, SSA/NCHS
anchor, presence / F6 / split-unit declarations) with >= 6 mutation checks
(tolerance digit, cell add/drop, OC drift, not_certified reorder, year pin,
history sha).

Sibling / manifest hygiene:
- tests/test_gate_m6_floors.py: the draft's untouched-guard inverts at the
  flip to assert gate_m6 is now LOCKED in gates.yaml (+ a verbatim-fidelity
  check: the live block equals the draft modulo the three deltas), mirroring
  the gate_w1 floors-test flip.
- tests/test_gates_derivations.py + tests/test_gate_w1_derivations.py: the
  added-locked-gate master-compare tolerance extended with {"gate_m6"} (the
  a1-era generalization), with attribution.
- tests/test_gate_w1_amendment2_proposal.py: the section-7 subset
  master-compare relaxed to master-side (tolerates the gate_m6 addition;
  every master gate stays present-and-equal in live except gate_w1).
- runs/legacy_manifest_v1.json: recorded via build_legacy_manifest.py
  --transition (only contract_revision + the gates.yaml entry move; the frozen
  legacy gate-2 entries stay byte-identical).
- tier recount (CI-equivalent full collection): artifact 928 -> 944 (+16, the
  new derivations file), total 2,668 -> 2,684 (tests/tier_counts.json +
  tests/README-tiers.md).

Depends on the #175 block-draft amendment (forward-law first-certification in
covers / not_certified + a design_commit re-pin), which lands before the
flip-fidelity round; a follow-up commit will re-apply the updated draft
verbatim + these same three lock-time deltas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* gate_m6 lock flip: re-apply block from #175-amended draft + finalize design pin

Follow-up to the #175 block-draft amendment (merged to master as ce9893b: the
design 2.7 forward-earnings law -- first-certified by gate_m6, gate_1 does NOT
cover it). Merges origin/master and RE-APPLIES gates.gate_m6 from the UPDATED
docs/design/gate_m6_block_draft.yaml, still VERBATIM, with the lock-time deltas:

- `locked: false -> true`
- `status: draft_cleared_ready_for_lock_flip -> locked`
- + the `2026-07-13-m6-gate-lock` history entry
- `design_commit` FINALIZED per the draft's own design_commit_note from PR
  #175's design commit `d6abb16b` to the #175 squash-merge `ce9893b`
  (`design_pr: "175"` carried verbatim).

The amendment carries into the live block verbatim: the extended `covers`
(forward-law first-certification framing) and the new `not_certified` margin
`forward_earnings_law_not_gate1_certified` (9 -> 10 margins). The history entry
records the finalization (`design_commit_finalized`: from / to / rule / note)
and narrates it in `content`.

Binding updates (`tests/test_gate_m6_derivations.py`): `design_pr "175"`,
`design_commit ce9893b`, the 10-margin `not_certified` count + the forward-law
margin, and the history finalization record are now bound; +2 mutations
(un-finalized design_commit; dropped forward-law margin) -> 12 mutation checks.
`tests/test_gate_m6_floors.py` verbatim-fidelity check extended to tolerate the
design_commit delta (live == draft modulo locked / status / history /
design_commit; design_pr stays "175").

`runs/legacy_manifest_v1.json` re-recorded via `build_legacy_manifest.py
--transition` (contract_revision + the gates.yaml entry only). Tier counts
unchanged (artifact 944, total 2,684 -- CI-equivalent recount confirms no drift
from the merge). Sibling gates byte-identical to origin/master (ce9893b);
gate_m6 the sole new key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* gate_m6 lock flip: re-finalize design_commit to the §2.7.6 (#178) squash-merge

Final re-finalization. §2.7.6 (PR #178, squash-merge 4c6a0f6, twice-verified --
VERIFIED comment 4960583620) COMPLETED the forward rank-to-level + re-ranking
laws -- the forward earnings law the gate_m6 `covers` references. Per the block
draft's own design_commit_note (finalize the pin to the squash-merge at the lock
flip), the live design_commit is RE-FINALIZED from the interim #175 squash
(ce9893b) to the #178 squash (4c6a0f6), so the lock references the COMPLETE law.

Merges origin/master (#178, the §2.7.6 design-doc completion) and regenerates
gates.gate_m6 from the (unchanged) draft VERBATIM with the four lock-time deltas
(locked / status / +history / design_commit). The history
design_commit_finalized record now carries from d6abb16 -> to 4c6a0f6 with
refinalized_from ce9893b and the §2.7.6 reason; the content narrates the
two-step finalization.

Binding updates (tests/test_gate_m6_derivations.py): design_commit 4c6a0f6, the
design_commit_finalized.refinalized_from + the §2.7.6 reason; the design-commit
mutation now drifts the stale interim ce9893b. tests/test_gate_m6_floors.py
asserts live design_commit == 4c6a0f6 (draft d6abb16 unchanged).

runs/legacy_manifest_v1.json re-recorded via build_legacy_manifest.py
--transition (contract_revision + the gates.yaml entry only). Tier counts
unchanged (artifact 944, total 2,684). Sibling gates byte-identical to
origin/master (4c6a0f6); gate_m6 the sole new key. Both design dependencies now
satisfied: #175 (forward-law margin + re-pin) and #178 (§2.7.6 forward-law
completion).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant