M6 design amendment: forward earnings generator spec (unblocks #173)#175
Conversation
Add §2.7 to docs/design/m6_projection_engine.md specifying the forward earnings generator the engine build (Sol, PR #173) correctly left external. The certified gate-1 candidate-11 chain is a BACKWARD biennial imputation (anchors the latest real earnings, predicts t-2 participation from later earnings, draws earlier ranks conditional on later ranks) and cannot be composed into the M6 forward loop without a new stochastic law. §2.7 specifies that law: - a biennial forward conditional-rank chain 2014->2016->2018 (scored on the {2016,2018} cells of §4.1), mirroring the certified conditioning structure reversed in time and fit <=2014 from scratch -- not an inversion of the backward conditional, no annual kernel for any gated cell, no marginal extrapolation (retiring Sol's three refused choices); - transfers k=25, distance weights 1/0.5/0.25, lambda=0.1 (Q0-exempt), the u_w shrunk-permanent rank, the RegimeGatedQRF participation gate, and the zero-anchor full re-entry pool from candidate-11/10; - newly chosen items (forward donor pools on <=2014 forward tuples, forward participation-gate fit, initial-2014 anchor, start-of-chain memory ramp) justified from <=2014 fit-window diagnostics only, candidate-blind with respect to the holdout (#42 comment 4948637741 spec-selection disclosure); - non-scored annualization (participation carried from the prior biennial wave; positive level log-linear interpolated on the NAWI-indexed level) feeding only the seam / AIME / claiming, never a gated cell; - certification framing: NOT gate_1-certified (different, forward law), first-certified by gate_m6; §4.2 refit enumeration + decision 10 updated; - implementation contract for the engine lane: inputs, outputs, RNG stream slots under the F3 (module, period) tree, and the CertifiedEngineInputs seam interface. Docs-only; no gates.yaml edit, no floor, no test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Adversarial review — forward earnings generator spec (revision 2, §2.7; commit
|
| claim | code check | result |
|---|---|---|
k-NN k = 25 |
run_gate1_candidate7.py:165 K_NEIGHBORS = 25 (imported byte-for-byte by c10) |
✅ true |
distance weights 1 / 0.5 / 0.25 |
candidate7:168-170 W_NEXT=1.0, W_NEXT2=0.5, W_ANCHOR=0.25 |
✅ true |
λ=0.1 donor blend, Q0-exempt (Q0 third term stays |u_A(donor)−u_A(target)|) |
run_gate1_candidate10.py:231 LAMBDA_FIXED = 0.1; docstring :28-35 (non-Q0 third term 0.1·u_w+0.9·u_A; Q0 memory-exempt) |
✅ true |
| RegimeGatedQRF sign-gate architecture | real populace-fit class — run_gate1_candidate2.py:96 import, :307 RegimeGatedQRF(seed=seed); gates via fit_participation_gate/_gate_sign_draw |
✅ true (external dep, correctly not in-repo) |
| zero-anchor full re-entry pool (no restriction) | candidate10:18 "no re-entry-pool restriction"; _reentry_draw from run_inner_sweep |
✅ true |
| weighted single-record draw, no smoothing/jitter | candidate7:383 _knn_draw |
✅ true |
| age as only other covariate (marital-blind) | prior round: 0 marital hits in c11; age_bin the covariate |
✅ true |
backward direction (anchor = chronologically latest; predict t−2 from later; draw earlier|later; two-step-plus-anchor) |
run_gate1_baseline.py:137 build_backward_pairs; candidate7:214,1105 anchor = "chronologically last observed period"; candidate10:255-267 "backward k-NN chain" |
✅ true |
u_w shrunk-permanent rank = "candidate-3 z-decomposition" |
candidate10:65-66 "the u_w decomposition is candidate 8's (PR #58)"; candidate8:27-38 u_w = Φ(ŵ/σ̂_w) built on "the SAME candidate-3 decomposition (Stage 1c correlated-noise)" |
The object that transfers (the u_w shrunk-permanent rank coordinate) is real and does transfer; only its lineage is loosely attributed.
Findings
1 — MAJOR. The §2.7.4/§2.7.5 implementation contract does not match PR #173's real earnings seam; the engine lane cannot implement with "zero further design choices." The actual slot the build left external is EarningsGenerator.generate(frame: pd.DataFrame, year: int, rng) -> np.ndarray (origin/m6-engine:src/populace_dynamics/engine/steps.py:164), invoked every projection year by apply_earnings(... model=inputs.earnings) (assembly.py:292-293; apply_earnings docstring "Draw annual earnings"). The doc instead specifies a different function — (realized_history, u_w, age, rng) → biennial_forward_draws, "a pure function ... with no engine state beyond its ≤2014 fit," consuming "RNG ... for each biennial step." Three concrete mismatches:
- Signature/abstraction: the real seam is per-year, frame-in /
ndarray-out. The doc's 4-named-input / biennial-bundle-out signature must be re-expressed asgenerate(frame, year, rng), stating which frame columns carryrealized_history,u_w, andage. - Annual call vs biennial draw:
generateis called for odd years too. The parity branch (draw on even, deterministic-fill on odd, §2.7.2) is implied but never connected to the real per-year protocol — the generator must branch onyearinternally, which the contract should state. - "No engine state" is false for a chain: the
2018step conditions on the drawn2016rank (rank_{t+2} | rank_t, rank_{t−2}⇒ for 2018,rank_t= drawn 2016), which the engine threads through the evolving frame (assembly._merge_period_columns), not "realized≤2014history." The contract's input list omits this prior-draw conditioning input and mis-states statelessness. Fix: restate §2.7.4/§2.7.5 againstgenerate(frame, year, rng), name the frame columns, specify parity branching, and specify that the prior drawn biennial rank re-enters via the frame.
2 — MAJOR. The in-flight gate_m6 block draft would lock with a covers that understates what a pass certifies. docs/design/gate_m6_block_draft.yaml (master 1818784, status: draft_cleared_ready_for_lock_flip, design_commit: 1d83a221 / design_pr: "170" — i.e. pinned to revision 1, pre-amendment) gates 6 earnings cells on gated_earnings_reference_years: [2016, 2018] and its headline-prominence not_certified/spec_selection_in_sample record covers only the surface selection (coarsening + decompounding). It nowhere records that (a) the earnings generator producing those {2016,2018} draws is the §2.7 forward law, first-certified here and not gate_1-certified, or (b) the forward generator's structural spec-selection (mirroring a full-window-selected certified spec, §2.7.1) — a limitation distinct from the surface-selection one already listed. A reader of the locked block would reasonably assume the gated earnings use the gate_1-certified generator; the amendment reveals they do not. Under the block's own amendment-1 standard ("a PASS statement must claim exactly the scored surface"), locking as-is misstates scope. The amendment updates only m6_projection_engine.md; it must flag as a lock-flip dependency that the block draft's covers/not_certified fold in the §2.7.3 forward-law framing and re-pin design_commit to df9ccb6.
3 — MINOR. u_w provenance mis-cited. §2.7 (and §10 JSON transfers_from_candidate11) attribute "the u_w shrunk-permanent rank" to "candidate-3 z-decomposition." The u_w = Φ(ŵ/σ̂_w) shrunk-permanent rank is candidate-8's construct (run_gate1_candidate8.py:27-38, PR #58); it is built on candidate-3's Stage-1c correlated-noise decomposition. c10 itself says "the u_w decomposition is candidate 8's (PR #58)." Credit candidate-8-on-candidate-3 (the coordinate transfers correctly regardless).
Checks that pass (recorded so the revision does not re-litigate them)
- ≤2014 derivability of every new choice (question 2) — SATISFIED. Forward donor pools (candidate-7's pool rule applied to
≤2014forward tuples), forward participation-gate coefficients (≤2014forward pairs, RegimeGatedQRF architecture/features transferred), the anchor flip (structurally forced — 2014 is the only real forward anchor), and the start-of-chain memory ramp (fixed from the≤2014wave calendar:2014→2016one-step-plus-anchor,2016→2018full two-step) are each determined by≤2014data + the mirror rule with no holdout-informed degree of freedom. The Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42 spec-selection disclosure is present and honest (out-of-sample for fitted parameters, in-sample for structural choice). Note: "candidate-blind wrt the holdout" is a design commitment the floors/pass-verification ceremony must confirm against the actual fit (no code exists yet). - Annualization isolation (question 3) — SATISFIED. The scored earnings cells read only the biennial
{2016, 2018}draws (block draftgated_earnings_reference_years: [2016, 2018]); the odd-year participation-carry / log-linear-interpolation values carrynon_scored_annualizationand feed only the pe-us seam / AIME / claiming, none of which is a gated cell (gated = marital/disability/mortality flows + biennial earnings moments). The interp-vs-carry flag is genuinely immaterial to certification (it moves only report-only AIME/benefit levels), so §2.7.2's "floors ceremony need not adjudicate it" is correct. - Certification framing, doc side (question 4) — COHERENT. §2.7.3, §4.2, and decision 10 consistently state no
gate_1certificate transfers and agate_m6pass is the forward law's sole certification; §4.2 correctly narrows the certified backward chain's admissible≤2014refit to plumbing, with earnings left external (confirmed:CertifiedEngineInputs.earningsis a standalone field, not infrom_refit_bundle's required bundle set{family, household, modifier, disability, claiming_pmfs}). The gap is only the block-draft artifact (finding 2), not the doc's internal logic. - RNG slots (question 5, RNG part) — code-true.
candidate10:239 SUBSTREAM_CODES {gate:1, donor-draw:2, re-entry-draw:3}and_substreamtwo-elementSeedSequence([seed, code])(:242-252) match §2.7.5's "three canonical sub-streams ... two-elementSeedSequence([·, code])draw order."
Read-only adversarial review; no repo edits, no merge/ready. Every cited line re-checked against df9ccb6, origin/m6-engine, and master 1818784.
Address the design-referee round on #175 (comment 4959673982, MAJOR REVISION); the forward law itself was verified code-true, the fixes are contract/record restatements: - finding 1 (seam contract): respec §2.7.4/§2.7.5 to PR #173's real EarningsGenerator.generate(frame, year, rng) -> np.ndarray seam (origin/m6-engine steps.py:164-169) -- per-year, called by apply_earnings every projection year, stateful THROUGH the frame. Name the frame columns read, the even/odd parity branch (biennial draw on even reference years, deterministic fill on odd), and the drawn-prior-2016-rank threading (recovered inside generate by re-ranking the carried earnings column, which _merge_period_columns threads forward). The forward per-year seam has no lookahead, so §2.7.2 annualization is corrected to a carry-forward default (log-linear interpolation only via batch-compute + backfill), which resolves the revision-1 interp-vs-carry flag; still non-scored (isolation unchanged). - finding 2 (block draft): update docs/design/gate_m6_block_draft.yaml -- covers and not_certified.forward_earnings_law_not_gate1_certified now record that a gate_m6 PASS first-certifies the §2.7 forward law (gate_1 does NOT cover it; no certificate transfers), including its structural spec-selection distinct from the surface selection; design_pr 170->175 and design_commit re-pinned off the stale 1d83a22 (note added; the hash is set to this PR's design commit). - finding 3 (u_w provenance): u_w = Phi(w-hat/sigma-hat_w) is candidate-8's construct (PR #58) built on candidate-3's Stage-1c decomposition; cited that way in §2.7 and the §10 ledger. Docs-only; no gates.yaml edit, no floor, no test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revision — design referee round addressed (commits
|
| # | Sev | Finding | Fix |
|---|---|---|---|
| 1 | MAJOR | §2.7.4/§2.7.5 contract didn't match PR #173's real EarningsGenerator seam |
respec'd to generate(frame, year, rng) -> np.ndarray (verified against origin/m6-engine:steps.py:164-169, assembly.py:86,292-293) — §2.7.4 |
| 1a | — | signature/abstraction (per-year, frame-in/ndarray-out) | §2.7.4 gives the Protocol verbatim + names the frame columns read (earnings, realized-2014 cols, u_w, age, sex) |
| 1b | — | annual-call-vs-biennial-draw parity | §2.7.4 parity branch: biennial draw on even reference years, deterministic carry on odd; §2.7.5 RNG consumed only on even years via the per-year person_generator(EARNINGS) stream |
| 1c | — | "no engine state" is false for a chain | §2.7.4 names the drawn-prior-2016-rank threading — recovered inside generate by re-ranking the frame's carried earnings column (threaded by _merge_period_columns); generator holds no cross-year state. §2.7.2 annualization corrected to carry-forward (the forward per-year seam has no lookahead; interpolation only via batch+backfill) — resolves the rev-1 interp-vs-carry flag |
| 2 | MAJOR | block draft would lock with an understated covers |
docs/design/gate_m6_block_draft.yaml: covers + new not_certified.forward_earnings_law_not_gate1_certified record that a gate_m6 PASS first-certifies the §2.7 forward law (gate_1 doesn't cover it; no certificate transfers) incl. its structural spec-selection distinct from the surface one; design_pr 170→175, design_commit re-pinned off stale 1d83a221 to this PR's design commit (d6abb16) + design_commit_note |
| 3 | MINOR | u_w provenance |
cited as candidate-8's u_w = Φ(ŵ/σ̂_w) (PR #58) built on candidate-3's Stage-1c decomposition — §2.7 transfer bullet + §10 ledger |
The corrected seam contract, in three sentences
The forward generator implements PR #173's EarningsGenerator.generate(frame, year, rng) -> np.ndarray protocol — per-year, frame-in / ndarray-out — called by
apply_earnings every projection year with each person's engine-owned
ProjectionModule.EARNINGS stream, and it is stateful through the frame, not a
pure function: generate branches on year, drawing the biennial forward value on
even reference years ({2016, 2018}, consuming the candidate-7 gate/donor/re-entry
substreams) and returning the deterministic carry-forward value on odd years, with
the return written to frame["earnings"] and carried to the next year by
_merge_period_columns. The chain's drawn-prior-rank conditioning (rank_{t+2} | rank_t, …) is not an argument but is recovered inside generate by re-ranking the
carried earnings column (0 = non-participation), with the earlier lag and anchor
from the realized-2014 columns, so no chain state lives outside the frame.
Block-draft delta
covers now states the gated {2016,2018} earnings cells ride on the §2.7 forward
law (a different stochastic law than the gate_1-certified backward chain, first- and
sole-certified by gate_m6); a new not_certified margin
forward_earnings_law_not_gate1_certified records this plus the forward law's
structural spec-selection (distinct from the surface-selection margin);
design_pr 170→175, design_commit 1d83a221→d6abb16 with a note that it pins this
PR's amended design and finalizes to the squash-merge at the lock flip. Verified:
tests/test_gate_m6_floors.py passes (23/23) against the edited block — mortality
drift stays named first, all six required not_certified margins remain.
Anything I could not verify against #173's code
Nothing. Every seam fact was re-derived from origin/m6-engine: the generate
signature (steps.py:164-169), the per-year call + frame["earnings"] write
(apply_earnings, steps.py:205-238), the per-person EARNINGS stream
(steps.py:212-224), the standalone external slot (CertifiedEngineInputs.earnings,
assembly.py:86; earnings_step, :292-293), and the cross-year threading
(_merge_period_columns, assembly.py:151-169). The only design commitment the code
cannot yet confirm — noted in the doc — is "candidate-blind wrt the holdout," which
has no implementation yet and is the floors/pass-verification ceremony's to confirm.
Verification round (round-diff) — forward-earnings revision (
|
…39799) Earnings is written in-place by apply_earnings and threaded by the loop's frame reassignment; _merge_period_columns threads the marital/disability/ household update-frames. Conclusion unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…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>
…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>
…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>
M6 design amendment — forward earnings generator spec (unblocks #173)
Adds §2.7 to
docs/design/m6_projection_engine.md(design doc revision 2),specifying the forward earnings generator the engine build left external. Docs-only,
DRAFT; no
gates.yamledit, no floor, no test.Why (Sol's honest blocker, PR #173)
The engine build composed steps 1–8 and passed the §2.6 re-certification margin
check, but blocked on forward earnings. The certified gate-1 candidate-11 generator
(
run_gate1_candidate11.py, byte-identical to candidate-10) is a backwardbiennial imputation chain: it anchors each person's chronologically latest real
earnings, predicts wave
t−2participation from later earnings, and draws earlierranks conditional on later ranks. M6 starts from realized 2014 and needs
forward earnings. Sol correctly refused to invent an inverse conditional, a
future-marginal extrapolation, or a two-year→annual kernel (each a different
stochastic law than gate-1 certified), shipping only the admissible
≤2014refitplumbing and an explicit external earnings seam (
CertifiedEngineInputsstillrequires an externally supplied earnings generator). This amendment specifies that
generator so the build can proceed.
What §2.7 specifies
2014 → 2016 → 2018(scored on the{2016, 2018}cells of §4.1; realized 2014 is the anchor).It mirrors the certified conditioning structure reversed in time and is fit
≤2014from scratch — not an inversion of the backward conditional; thescored path stays biennial (no annual kernel for any gated cell); it applies
a
≤2014-fit conditional out-of-sample rather than extrapolating a marginal.This retires all three of Sol's refused choices. Transfers from candidate-11/10:
k-NN
k=25, distance weights1/0.5/0.25, fixedλ=0.1donor blend (Q0-exempt),the
u_wshrunk-permanent rank, the RegimeGatedQRF participation sign gate, thezero-anchor full re-entry pool, the weighted single-record draw. Newly chosen
(justified from
≤2014diagnostics only, candidate-blind wrt the holdout, per Candidate 2 design: latent-permanent conditioned chained QRF (research memo) #42comment 4948637741): the forward donor pools on
≤2014forward tuples, theforward participation-gate fit, the initial-2014 anchor, and the start-of-chain
memory ramp.
{2015, 2017, …}are filled non-scored(participation carried from the prior biennial wave; positive level log-linear
interpolated on the NAWI-indexed level between biennial brackets), feeding only
the pe-us seam / AIME / claiming. The scored earnings-moment cells read only
the biennial forward draws.
law than gate-1 certified, so it is not covered by
gate_1and isfirst-certified by
gate_m6; §4.2's refit enumeration and decision 10'scertified-claims language are updated to record this (no
gate_1certificatetransfers to it).
≤2014history,
≤T*latent-permanentu_w, age, injected RNG), outputs (biennialforward draws for
{2016, 2018}+ the non-scored annual series), RNG streamslots under the §3.2 F3
(module, period)tree (consumed only at biennial steps,spawning candidate-7's gate/donor/re-entry sub-streams), and the
CertifiedEngineInputsseam the build (M6 engine: wave-loop composition per verified design (build only) #173) left external.Not in scope
No
gates.yamledit, no floor artifact, no test, no code. The forward generator isimplemented by the engine lane against the §2.7.4 contract and first-certified in
the
gate_m6scored run.🤖 Generated with Claude Code