Repository navigation
Declare the NZ transport and entitlement graphs - #1440
Conversation
Declare June 2025 ERP mass, the version 2 receipt contract, pre-candidate solver settings and generated reference exemptions. Activate supported MSD receipt targets and distinct national recipient and historical Accommodation Assistance comparators. Remove the rent-stock scenario, bind gross paid-benefit reporting and label optional export receipt flags as assigned state. Check active-or-exempt partitioning and exact exemption statuses exhaustively in test_nz_transport_declarations. Check required-reference protection, active receipt targets and bound filters in the same module. Prove disjoint age intervals from their declared bounds and enumerate ages across the working-age boundary. Compare every scenario and gate list, require unsupported VP and verify the separate count and dollar measures. Exercise generator output through --check and retain every inactive-row refusal when reference_selection is absent. Replay the pre-activation reconciliation from pinned aggregate facts and published rates with exact rational arithmetic. Pin recipient selectors through canonical aggregate_fact_key fields and check their shape exhaustively so the existing spec-only scanner recognizes identifiers. Record headline-rate envelopes and exposure sensitivities while leaving category-weighted after-abatement totals unavailable until the hub obtains joint categories, paid amounts and exposure evidence. Preserve engine-pin, graph, export-enforcement and release-evidence handoffs. Regenerate the country golden with the repository summary generator and restrict its diff to the fingerprint, hashes and five added resources. Kill all ten named mutants with their declared invariant tests against fresh scratch copies, and prove each copied import through compose.__file__. Preserve the holdout leakage property while correcting its synthetic ratio selector to retain wildcard periods; add deterministic cases for the newly pinned holdouts. Record each validation file's passing counts, failures and outstanding checks in the handoff report. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The small-frame matrix test still expected 234 active rows and built a frame with no receipt flags and no adult aged 18 to 24, so CI failed on the 12 receipt cells and NZ Super. Give that frame one adult in the youngest band and the four flags takeup.assign@1 writes, and expect the 109 rows that stay active. Pin H19's exact solver settings in the declaration test, because a changed epoch count passed it before. Correct three descriptions: the A1 method now names the gross payment columns and H4's Veterans Pension exclusion, annual-transfer exposure lives in benefit_gross_up.json, and export.prepare@1 emits a descriptor that materialize_export writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local run of the NZ test files, with no failure cap, found two tests CI hadn't reached. The IRD rows' parking reason now names G8-r2c, and removing S4 leaves nine scenarios, so the cell flags number 43 times 9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generate transport_graph.json, entitlement_graph.json and their supporting resources, including independent variant branches and a hold-fixed sibling money declaration. Add an opt-in closed roster check on export and terminal gate slices, and admit gt and finite nonnegative numbers in calibration predicates. Work in progress: the build lane stopped before its final validation, and packaged composition still needs Reg 17/18 assembly and a person region producer. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
G8-w3a landed as a squash, so git saw six files that both sides touched as add/add conflicts. Main's copy of each equals w3a's head b32593d, and main changed no file w3b changed, so w3b's side resolves all six. test_transport_weight_quality.py needed the same by hand: w3b's 43 * 10 returns that pin to its pre-w3a value, so the three-way merge took main's 43 * 9 without a conflict. git diff main HEAD now equals git diff b32593d 2b44c12 byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Cloud session picked this up at 362f96e; it holds the branch until it posts its hand-off. Generated by Claude Code |
H28: the NZ gross-up now reads whole-cent nets. A money.minor_units@1 node, nz.benefits.net_cents, rounds each JS, SPS and SLP weekly net half-even to cents ahead of its solve, and each solve's source term reads the cents column with coefficient 0.52. The node carries the main_benefits_rates reference, so composition attributes its outputs to the rates stage, which now lists them as derived outputs. benefit_gross_up.json discloses the rounding. No kernel changes. The rewritten test runs the packaged cents node and then the packaged solve: rows 801, 802, 901 and 902 all solve and equal the exact half-even inverse of the rounded net. A companion test shows the unrounded 901/902 tie still refuses when a solve reads the net directly. A Hypothesis property checks H13 item 5's bound: for every cent-exact net the half-cent boundaries next to the exact root keep |F(b)| >= 1/(200q) >= 2.5e-5, 25 times the margin. A second property solves generated four-decimal nets against the oracle. H27 as amended by H27a: geography.person_location@1 lives in the new transport/person_location_kernels.py. It projects a closed canonical map of household string columns to person string columns through household membership, keeps person row order and count, and refuses a missing household value or a membership that names no household. transport/registry.py gains one import and one call; compose.py gains one SKELETON_KERNELS line; graph_atomic_geography.py stays byte-identical. A hash script over the transport registry showed the registry edit moves no hash and adds only geography.person_location@1. Node nz.geo.person runs on nz.open after nz.geo.derive and writes person.region_code. Tests: test_nz_benefit_gross_up.py 16 passed, test_atomic_geography.py 44 passed, test_nz_transport_declarations.py 25 passed and 2 skipped, test_nz_msd_benefit_rates.py 6 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
|
M1 is done at 311b814.
Next: M2, the reg 17/18 bridge (H23, H24). Generated by Claude Code |
H23: as_rate_bridge.json now declares reg 18 as method solve_zero. A simulate.counterfactual@1 node evaluates jobseeker_support_gross_weekly_rate under the resource's gross_rate overrides and writes it for sole parents only. A simulate.solve_zero@1 node then solves jobseeker_support_total_income on [0, 5000] with tolerance 1e-9 and 43 iterations, with three family terms: the net payment for other units, minus the reduction for sole parents, and the gross-rate column. jobseeker_support_single reads a derived single, no-dependent-child column, so sole parents fall to Income Test 3, as reg 18(3)(b) requires. The resource keeps the harness expressions as its equivalence statement and drops "awaiting D1". H24: reg 17 is one counterfactual over main_benefits_rates and family_tax_credits: the zero-income net payment once for a single unit and twice for a partnered one, plus the prescribed amount over 52 for a unit with a dependent child. The youngest-child override carries a mask on the has-child column, so a childless family keeps the closure default 0. One calibration.prepare@1 node per population writes the two family masks. Placement: an extension node on nz.as re-keys the skeleton's nz.terminal FILTER, and compose.py refuses that, so the shared masks, gross rate and cutout run on nz.as.bridge, a FILTER branch of nz.as that S0, S1, S2, S3 and S5 descend from. Each variant repeats them on its housing population, and every branch runs its own base rate. The PR carries a hub-ruling request on this placement. New test_nz_as_bridge.py runs the packaged declarations through the real kernels on labelled fixture engines that copy the formula shapes. It checks: golden-08's base rate and cutout within 1e-9; Hypothesis properties for the sole-parent cutout L + G/s and the other-unit anchor extrapolation; Income Test 3 for sole parents; the gross-rate overrides over the unit's own schedule; the sum rule for the base rate; and the masked youngest-child override. With pandas 3.0.3 the unmasked override hands the engine NaN, whose int64 cast raises under numpy's invalid-value check. The packaged graph now composes with only engine pins and the three money producers added, and CREATE declares no bridge or region column. Tests: test_nz_as_bridge.py 14 passed, test_nz_transport_declarations.py 24 passed and 2 skipped. test_nz_axiom_input_closure.py keeps the same three as_area dtype failures it had at 362f96e. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
|
Hub ruling needed H24 places Interim at 246ae91, easy to undo: the three shared nodes run on M2 is otherwise done at 246ae91: reg 18 runs through Generated by Claude Code |
|
Hub ruling H29, on the placement question above. Please apply this at your next convenient point in M3 or M4. Confirmed: keep the interim at 246ae91. Two additions:
If you've already passed this point, apply it before your hand-off. Everything else in the brief stands. |
H25: rulespec-nz's rounded AS payment never reads the module's non-rate
entitlement judgment, so each branch's positive_payment node now holds only
where the payment exceeds zero and the judgment equals 1, the code the Axiom
adapter's decoder gives "holds". Undetermined (0) fails. A new
{prefix}.as.entitled counterfactual reruns the AS engine with no overrides
and writes accommodation_supplement_entitled_weekly_payment, the rounded
payment masked on that indicator. H2, the reporting measure, the gap node's
inputs, cell declarations, bands and gates read the entitled column, while
H1 keeps the indicator. The column joins export_contract
formula_owned_excluded and the formula_owned_export gate's list. The target
reference generator reads it, and holdout_references.json carries the same
four edits by hand; the hub runs both generators' --check.
H26: every branch but S5 adds receipt_group in [beneficiary, other] to the
conjunction (holdout_comparator_bindings central). That one predicate
defines the excluded set, so it equals the superannuitant cell, and
reporting_classifiers.json lists the structural zero for V1 to V4 and VHF as
well. S5 selects the s5 bindings without that clause. Its own masks node,
run after its receipt_group node, crosses single and partnered with
superannuitant, and its base rate takes nz_super_weekly_amount_before_tax
(max, coefficient 1 or 2) for superannuitant units. as_rate_bridge.json
states the before-tax reading and the mixed-couple simplification.
Tests in test_nz_as_bridge.py run the packaged positive-payment, entitled,
receipt-group, mask and base-rate nodes on labelled fixture engines:
failing and undetermined judgments read zero; S1's asset-test knob, through
InputClosure.group_knobs, lifts a unit over the asset limit that S0 zeroes;
a superannuitant unit reads zero in S0 to S3, V1 to V4 and VHF but keeps its
payment in S5; the excluded set equals the superannuitant cell; S5 pays from
NZS with coefficient 2 for a couple. test_nz_spec_package.py also gets
fixes for four failures that predated this session: VHF in the scenario
list, the concept-mapping format id, and the annual-gross classifier
columns.
Tests: test_nz_as_bridge.py 32 passed, test_nz_spec_package.py 1203 passed,
test_nz_transport_declarations.py 24 passed and 2 skipped,
test_nz_target_references.py 16 passed and 38 skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
|
M3 is done at 0e457e9.
Next: M4, packaged composition, the toy run and the golden. Generated by Claude Code |
The packaged spec's activation preflight names exactly the two engine pins
and the three pending money quantile maps, so _GAPS in
test_transport_composed_graph.py stays as it was. A new test shows
_check_export_contract needs no id or membership column in a slice:
compile_graph refuses one with "reads household.household_id, which no
node owns", because the executor projects ids for every sliced entity.
The country-spec golden now carries the settled resource hashes, from the
brief's regeneration script. Every NZ JSON resource already appears in
country_package.json.
Two test fixes cover failures that predated this session. The toy
pending-money test now matches the preflight's actual message ("awaits a
donor that carries it"). The axiom input closure tests now give as_area as
the string codes the state bindings compare.
Tests: test_country_spec.py 143 passed, test_nz_axiom_input_closure.py 17
passed and 1 skipped, test_transport_entitlement_graph.py 67 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
The test cut the export slice to person.age alone. That dropped the household and family entities, so the executor projected none of their ids and the earlier "required columns" refusal fired first. The test now drops one person roster column and keeps every entity, so only the exact-slice check can refuse. This failure predated this session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
Two tests in test_nz_money_normalization.py failed on this branch before the main merge. They asserted that no graph reads the normalized money and that the exchange rate lives in one resource. The packaged graph now reads donor money, so the preflight test asserts the three pending quantile maps by name. The VHF sibling declaration copies the central rate, so the rate test lists both locations and checks the two rates are equal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FLymaTkqTaw45hgwZ
|
Ready for hub review Head: Milestones and rulingsM1 (311b814)
M2 (246ae91)
M3 (0e457e9)
M4 (15fa011, cab751b, merge 5e14e25, daeafb1)
Activation gaps at head
Shared hunks (head versus origin/main)
Hash table. Each tree ran in its own process, comparing merge-base All 37 kernels
Tests at head, after the mergeEach file ran on its own,
Skips:
Also passing: Failures that predated this session, now fixed in the tests only:
MutantsAll 22 are red. Each ran on a copy under
Invariants and the tests that run them
Memory: the two CREATE executions (obligation 7)
Judgment calls
Not done, for the hub
Nothing was cut from the mutant list. Generated by Claude Code |
Ruling H29 keeps the three shared bridge nodes on nz.as.bridge, a FILTER branch of nz.as, because any node on nz.as would re-key nz.terminal. The cloud build placed them there but never saw the ruling, so its two additions land here. scenarios.json's entitlement branch point now names nz.as.bridge. The synthetic NZ toy takes the packaged shape, so its scenarios descend from the branch. Two new tests hold the ruling: the FILTER keeps every person, unit and weight, and nz.terminal keeps the skeleton's own predecessors, with no bridge node anywhere in its ancestry. Local pytest waits on free disk, so CI runs these first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The golden pins every resource's hash, so H29's scenarios.json edit moves that hash and the spec fingerprint. Nothing else in the golden changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # packages/microcosm-build/src/microcosm/build/transport/compose.py
_check_export_contract read benefit_unit_rule directly, so a create@2 spec with an export contract refused for the missing resource. A stale legacy rule beside two declared partitions was worse: the check took the legacy schema and missed the second membership column, which the export node still receives. Belgium plans a create@2 spec with a contract, found this by running it and supplied the fix. The check now takes the schema from the CREATE declaration, with the same fallback as the relation check, so create@1 behaves as before. New tests cover one declared unit with no legacy rule, and two partitions with and without a stale rule, for a silent, allowing and forbidding roster. No kernel attests compose.py, so no hash moves. CI runs these first: local pytest waits on free disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Belgium's run at a873b7b found two failures on this tree that main doesn't have. CI couldn't show them, because the engine-free run stops at its first failure. - A packaged concept mapping spells each binding's RuleSpec file as `module`. ConceptMapping.from_dict reads that field and the transport kernels select bindings by it, so the entrypoint scan now admits it, only at a concept_mapping.json binding and only for a country's RuleSpec YAML path. A new test shows every other shape stays refused. - NZ's closed legacy_json roster gains the eleven resources this PR declares. No test ran the graph builders' --check, so a later edit to the adapter's nz.json would have left the packaged mapping behind. The new test needs no artifact, and a mutant of nz.json on a copy turns the check red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Belgium's check of rulespec-be found 32 of its 116 rule files outside the scan's path pattern: regional trees such as be-vlg/ and one article with a hyphen. Widen the pattern to those two shapes. A segment still can't hold a dot, so .. stays refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Ready for hub review This report comes from the NZ hub's cloud review of the finished build. It isn't the hub's verdict: the hub reads it and decides. VERDICT: APPROVE_WITH_NITS Base Findings, most severe firstNo P0, P1 or P2 finding. Every ruling H23 to H29 holds where I tested it. These are P3 items and nits.
1. Reg 18 cutout (H23)
2. Reg 17 base rate (H24)
Youngest-child override. For a childless family: the family column is Family shapes. I found no shape that gets a base rate other than what the ruling and the harness define. The age-25 constant for under-25s stays the open item the resource discloses. A sole parent whose youngest child is 14 or older takes the 14-or-older rate in reg 17 (the unit's own youngest age reaches the base rate), while reg 18(3)(b)'s gross rate pins age 0; that follows the harness on both sides. 3. Entitlement gate (H25, H26)
4. Person location (H27, H27a)I read 5. Cents rounding (H28)
6. Placement (H29)
7. Shared code
8. Hash claimHolds. I built the registry the way 9. Test strengthEach mutant ran on a copy under
No test I read asserts over an empty set. 10. Claims
11. Delta since the hand-off
12. The whole engine-free list, with no stopI copied Final line:
Closed-list counts from this run: I fixed nothing and pushed nothing. Generated by Claude Code |
|
Merged by the NZ hub (session local_e407a527) at e1d2707 with --squash. Gates: |
* Fix the NZ bridge doc and tests the #1440 review flagged The bridge paragraph predated H29 and never named nz.as.bridge. The childless-family tests cast the column themselves and claimed the adapter raises, but the masked override returns float64, so _batch_from_table takes its float branch. Both tests now read that function. A new test runs a mixed couple through S5's base rate in both person orders, because a `first` aggregation passes one order and fails the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Correct the other stale bridge claims the review named The generator's docstring and one test name repeated the wording the doc paragraph lost, and the graph never matched it. Two as_rate_bridge.json reasons gave the adapter's int64 read as why the youngest-child override takes a mask, which the childless-family tests now show doesn't happen on this path: a null age would reach the engine input as NaN. The golden moves only for that resource's hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the last wording findings on the NZ bridge review The changelog credited the generator docstring with a change this PR did not make: the docstring already named the shared population, and the PR only dropped the sentence that contradicted it. The docstring's "every coefficient" holds for the bridge alone, so it now says so. benefit_unit_rule.json said the youngest-child age column holds 0 for a childless family. unit_construction.py writes a null there, which is why the bridge override takes a mask, so the description now says null. The golden picks up that resource's new hash and the fingerprint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Status
The build finished at
daeafb1c. The hub then added ruling H29 in182cd234, merged main at6edab2ac, refreshed the golden in3e2181c0, took Belgium's S1 at2c2b467f, made_check_export_contractread the declared units ata873b7b5(Belgium's fix for acreate@2spec), cleared two roster failures atfe33541b, widened the scan's path pattern atbac6dc22and merged main ate1d27077.Correction. An earlier version of this body said CI at
6edab2acpassed 7,731 engine-free tests, "H29's among them". That run showed far less.tools/run_engine_free_tests.shpasses--maxfail=1, so it stopped at its first failure, the golden comparison, and the files after that never ran. Belgium's run found two more failures past that stop, and the hub fixed them atfe33541b.CI at
fe33541bthen ran the whole list. Python 3.13 and 3.14 each gave 24,143 passed, 250 skipped and 3 xfailed, none failed, and the other eight checks passed. These logs print progress dots, not node ids, so they can't name a test. Both H29 tests sit in files on the job's list and carry no skip mark.Results at
e1d27077:calibration.prepare@1andtakeup.assign@1move from main, andgeography.person_location@1joins. The threesimulate.*kernels stay unchanged. Belgium reproduced this.am,beandnzstay byte-identical. All three builders'--checkand both generators'--checkpass, the generators against the hub's local artifact.atomic_geography_support.jsonfrom pinned sources, and this PR changes neither that file nor its builder.e1d27077on main3100f311, with its five Belgian dumps byte-identical to main. It also ran the whole engine-free list with no stop ata873b7b5, on 3.14, and found no real failure.Independent review: a cloud Opus 5.5 session reviewed
e1d27077on main3100f311and returned APPROVE_WITH_NITS, with no P0, P1 or P2 finding and nothing for Max. Its own hash run shows the same moves. It ran the whole engine-free list on Python 3.13 with no stop: 24,145 passed, 249 skipped, 3 xfailed and 1 failed. That failure,test_unreadable_staging_subtree_aborts_before_any_marker, needs a non-root user. The container runs as root, and the test fails the same way on main.The hub checked each finding against the code:
docs/transport-entitlement.md, and no mixed-couple test through S5: all confirmed. A follow-up PR fixes all three.geography.derive@1can't write"", because_bindlets a layer read onlycodecolumns anddecode_atomic_supportrefuses empty codes.GPT-6.1 Sol's queued review never started behind the disk brake, and it pinned an older head, so the hub cancels it.
Still open: CI at
e1d27077, where the two engine-free legs are running and every other check passed.The build's hand-off report gives file and line for every ruling, the kernel hash table, the mutant table and its test results. It lists H24's placement as open, because that session never saw H29.
What changes
This is G8-w3b, the NZ graph declarations:
transport_graph.jsonandentitlement_graph.json, generated bytools/build_nz_transport_graph.pyandtools/build_nz_entitlement_graph.py.simulate.solve_zero@1and the reg 17 base rate as one counterfactual (H23, H24).nz.as.bridge, a FILTER branch ofnz.asthat carries the shared bridge, because a node onnz.aswould re-keynz.terminal(H29). S0, S1, S2, S3 and S5 branch from it.accommodation_supplement_entitled_weekly_paymentis the new measure.geography.person_location@1, in its own module, sograph_atomic_geography.pystays byte-identical (H27, H27a).Shared code
transport/compose.py: theSKELETON_KERNELSline, theexport_contractdocument field,_check_export_contractand its two calls.calibration_predicates.py:gtandnonnegative_number.transport/registry.py: one import and one call.transport/person_location_kernels.py.test_spec_only_country_packages.py: the entrypoint scan admitsmoduleonly at aconcept_mapping.jsonbinding and only for a country's RuleSpec YAML path.ConceptMapping.from_dictreads that field and the transport kernels select bindings by it, so a rename would break the format.The build reports these hash moves:
calibration.prepare@1andtakeup.assign@1move, andgeography.person_location@1joins.Invariants
nz.as.bridgeholds the same persons, units and weights asnz.as, andnz.terminalkeeps the skeleton's own predecessors, with no bridge node in its ancestry.build_nz_*.py --checkwithout the artifact, so the packaged concept mapping stays equal to the adapter'snz.json.For G8-x, with the real engine
family_tax_creditshas 21 root inputs. The bridge sets two, and none sit inaxiom_input_closure.json. The adapter omits inputs the table doesn't carry and the engine errors on required ones, so the first real run must show the module evaluates.youngest_dependent_child_agereaches the engine as float64 under pandas 3.0.3. The real engine must accept that column for an integer input.money.minor_units@1rounds each net's shortest decimal, which can't undo error the engine made first. In float64,528.32 - 0.7*0.05gives528.2850000000001, and the node writes 52829 cents; half-even on the exact 528.285 gives 52828. That costs one cent a week, 0.52 a year in the gross-up, and only if the pinned engine computes nets in binary floating point. The first real run must show which arithmetic the engine uses.🤖 Generated with Claude Code