Skip to content

Gross up NZ benefit nets through certified engine solves - #1434

Merged
MaxGhenis merged 5 commits into
mainfrom
nzhub/g8w2b-grossup
Nov 1, 2026
Merged

MaxGhenis merged 5 commits into
mainfrom
nzhub/g8w2b-grossup

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Status

Ready to merge. This head carries the two-direction ownership check from round 6's review, and Belgium's test-only be-followup-2.diff, which Belgium's review of its follow-up asked for. Belgium has OK'd every shared hunk here. GPT-6.1 Sol, the hub's independent reviewer, approved this head in round 9 with no findings, and all ten CI jobs passed.

What changes

  • Solve-zero: simulate.solve_zero@1 gains closed affine source and trial terms and opt-in quantization. Quantization needs all five params: quantum, tie_rule, quantized_residual_bound, extra_evaluation_budget and certificate_margin. Quantized refusals name the affected rows for data columns.
  • Money kernels: money.minor_units@1 converts declared source columns to owned integer multiples of a declared quantum. money.annual_transfers@1 forms annual transfers from flags, cents, declared weeks and explicit exposure.
  • Composition: the skeleton admits those three kernels and the two-key binding selector. A money bridge's column has one owner across its population lineage: a bridge can't take an existing column, and no later node can take a bridge's. With money_normalization declared, only money.normalize@1 writes a normalized column's integer companion.
  • NZ: declares the Schedule 1 gross-ups, NZ Super's before-tax path and the S5 pre-calibration outputs, and checks them against pinned MSD benefit rates. This change adds no population-graph wiring.

Invariants

  • Round trip: on each of the 14 individual MSD validation points, the solved annual gross, converted to cents and carried through annual transfers, reproduces the published weekly gross. The test runs through ScheduleFixtureEngine. The resource pins 21 weekly rows, and the 7 others are couple totals.
  • Certificate: a quantized answer sits between half-boundaries whose signs differ beyond certificate_margin, or, at m = 0 only, on the even neighbour of an exactly-zero boundary. |F(k·q)| + m stays within quantized_residual_bound in exact arithmetic.
  • Exact cents: money.minor_units@1 takes the integer multiple of the quantum from the float's shortest decimal, so a binary64 product never decides the cents.
  • One owner: composition refuses two owners of one column in a population lineage when either is a money bridge. execute_transport_graph runs the graph prepare_transport_build checked, and run_graph applies no transport policy.
  • Preservation: unquantized declarations that use only the earlier term forms keep their outputs, receipts and refusals, except the solve-zero permitted-fields list, which now names the five params.
  • Determinism: a fixed input gives fixed evaluation counts and bytes.

Property tests cover the inverse and round trip, the certificate under bounded engine error, determinism and half-even exposure. Example tests cover the overflow bounds. Literal pins hold legacy solve-zero and counterfactual behaviour, and review ran a 776-comparison differential against base code outside this repo. A differential test compares the skeleton selector with entitlement._binding. One difference is intended, and a test labels it: only the skeleton selector needs a text binding id, so it alone refuses a blank or non-text one.

Impact

  • Implementation hashes: simulate.rules_by_ref@1, simulate.counterfactual@1, simulate.solve_zero@1 and money.normalize@1 move, and toy nodes re-key.
  • UK and US: no runtime node re-keys at fixed inputs and dependency versions, because their source closures omit these modules.
  • BE and NZ: neither has a packaged executable graph at this base. BE's five dumps stay byte-identical, and the NZ spec fingerprint changes with the new resources.
  • Aggregates: nothing changes, because nothing wires these kernels into a published graph.

Validation

At ac08f191, on main b979d46:

  • ruff check, ruff format --check, git diff --check, tools/ci_test_plan.py verify, tools/graph_acceptance_burndown.py --verify and both generators' --check pass.
  • CI stops at the first failure and runs the largest files first, so the runs at earlier heads left the smaller files unrun. The 39 unreached files that touch anything this PR changed, plus the file CI failed on, gave 764 passed, 2 skipped and 1 failed. That failure, test_normative_mutation_changes_hash_and_names_legacy_payload_field, fails identically on unmodified main in the hub's environment, from its editable install. CI's full engine-free run covered the other 457 files, and that test passes there.
  • Run serially at 97fe16ab: test_transport_gate_bindings.py 126 passed, test_country_spec.py 136, test_transport_annual_transfers.py 74, test_transport_grossup_selectors.py 26, and the composed money_bridge cases 11.
  • Mutants at 97fe16ab all died: no reverse direction (8 failed, and 2 of the 3 composed cases), no forward direction (4 failed), any two owners (1 failed), no companion condition (3 failed), and review's or to and (12 failed).
  • BE re-clear at 976664aa, with dump_be.py against main and against main plus Rewrite the Belgian country package for donor transport #1369 at 55ac91b: all five dumps IDENTICAL, golden equal, and the three BE suites pass with 22, 137 and 28.
  • At this head: test_transport_annual_transfers.py and test_transport_population_kernels.py together 107 passed, and the composed money_bridge, later_owner and normalized_companion cases 15. Belgium's mutants of the two normalization calls each died on their own declared case.
  • CI at earlier heads: 09e1f084 failed on the gate list, and 97fe16ab on the registered-kernel pin. This head carries both fixes.
  • CI at this head, run 41284943016: all ten jobs pass. engine-free gave 23,405 passed, 242 skipped and 3 xfailed on both Python 3.13 and 3.14.
  • Sol round 9 at this head: the thirteen-kernel test passed, and removing registry.register(MONEY_ANNUAL_TRANSFERS) in a clone failed it. Removing either normalization call failed only its own declared case, and a companion check that ignores entity failed only the household positive.

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 31, 2026 22:25
Extend solve-zero with closed affine source and trial terms. Require an
explicit decimal certificate margin that bounds residual error at every
evaluation. Certify quantum answers through half-boundary evaluations,
refuse ambiguous signs and plateaus with row ids, allow half-even ties
only for exact-output contracts, and include the margin in exact
residual bound comparisons. Name every affected row when source
conversion, coefficient overflow or underflow, integer-unit limits,
bisection precision, missing masks, or final float64 representation
force a quantized refusal. Enter integers exactly and floats through
their shortest decimal text. The row-naming contract covers data columns.
Refuse missing or invalid quantized masks with their source-row ids.
Check quantized membership lookup once before evaluation and name person
rows whose memberships miss group ids. Keep ordinary refusal text.
Preserve legacy normalization, solve outputs and receipt bytes. Check
exact ties, bounded engine error, large integers, plateaus, non-finite
boundary outputs, final answer representation and fixed evaluation
counts. For declarations that use only the earlier term forms, preserve
legacy refusal messages except the solve-zero permitted-fields
diagnostic, which now lists the five quantization names; retain the
counterfactual list. Compare legacy outputs, receipts and refusals with
the assigned base and require exactly that exception.

Recover authoritative integer cents from float64 bridge answers and
compute annual transfers directly from those cents with explicit
exposure. Check half-even fractional exposure, binary flags, finite
values, boolean amount refusals, int64 overflow and exact receipt sums.
Keep weekly gross reporting separate from persisted graph outputs and
annualization. Refuse a bridge output that its population lineage already
owns, at both composition passes, and let only money.normalize@1 write a
normalized column's integer companion, because the kernels read a
projection that leaves out an inherited column.

Declare the pinned Schedule 1 binding and solver-owned taxable income,
the three after-abatement net gross-ups, NZ Super's before-tax path and
S5 pre-calibration outputs. Check the generated declarations through the
real composer, require every node carrying a stage reference to keep
structural: none, and retain private-output refusals. Admit only the
settled two-key binding selectors in the skeleton.

Pin MSD source provenance and all published rate pairs. Check all 14
individual benefit points through the engine solver, cents conversion
and annual transfers against both MSD weekly grosses and an independent
exact inverse. Keep real-engine validation and population-graph wiring
with their assigned lanes. Declare the NZ certificate margin as 0.000001
and check exact half-cent boundary clearance over all MSD nets and dense
and random cent-exact nets across every Schedule 1 bracket. Publish
pinned RuleSpec and MSD citations alongside the method, and guard the
added resource text against private review labels. Describe closure
defaults as modelling choices with reasons and citations. Verify
generator freshness, country-spec goldens, targeted regressions, lint,
test-plan ownership and the graph acceptance ratchet.

Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 6's review found the reverse of the hole the Belgium follow-up closed. A
later node could own a bridge's column in a new population version, composition
accepted the graph, and execution replaced the cents with no refusal.
`_check_money_bridge_owners` now walks the lineage of every owner and refuses
when either owner is a bridge.

Add `individual_income_tax_before_credits` to the NZ `formula_owned_export`
gate so it matches the export contract. That fixes both engine-free CI failures.
Regenerate the NZ golden.

Reword the `money.minor_units@1` docstring, match the companion test on the
companion refusal so its bridge cases can fail, and extend the selector's
intended-difference test to non-text binding ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`register_transport_population_kernels` now installs thirteen kernels, because
`register_money_kernels` gained `money.minor_units@1` and
`money.annual_transfers@1`. Update the test that pins the set, and the
docstring that counted eleven. Neither moves a kernel hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing failed when either `_check_single_money_normalization` call left
`compose_transport_graph`, because activation refuses the toy fixture once a
spec declares the resource. Add a composed test on both passes, declared and
undeclared, and an other-entity positive for an entity-blind companion check.

This applies Belgium's `be-followup-2.diff` byte for byte. It changes tests
only, so no kernel hash moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 1, 2026 09:40
@MaxGhenis
MaxGhenis merged commit 4e30332 into main Nov 1, 2026
10 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merged by the NZ hub (session local_e407a527) at ac08f19 with --squash. Gates: gh pr checks exit 0; MERGEABLE; not draft; no CHANGES_REQUESTED review. Reviewed head: ac08f19. Independent review: GPT-6.1 Sol round 9 returned APPROVE at this head with no findings. Belgium, which co-owns the shared transport hunks, OK'd every one of them, including its own test-only follow-up, applied byte for byte. CI run 41284943016: all ten jobs pass, and engine-free gave 23,405 passed, 242 skipped and 3 xfailed on Python 3.13 and 3.14. The BE re-clear at 976664a matched all five dumps alone and with #1369; ac08f19 adds tests only.

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