Repository navigation
Bind NZS co-residence from household composition (H2) - #1485
Conversation
Belgium's hub prepared these two hunks for H2 (R2C-20). The shared row-by-row reference (test_support/microcosm_frame/concept_reference.py) gains a CoresidentAdult case: a person is true when their household id is present and another person row with that household id has a present age at or above min_age. Without the case, reference_values reaches its "no reference for" AssertionError on any coresident_adult binding, and test_encode_matches_a_row_by_row_reference runs over every committed mapping. tools/build_nz_transport_graph.py adds coresident_adult to the transforms whose closure dtype is bool, so closure_entry_dtype returns "bool" for the binding instead of raising "unsupported transform". The diff applies to this base with the builder hunk two lines lower, and `git diff | git patch-id --stable` printed e3f8d1c790817d1c3e6677e68fd1f0a564f68af2 before this commit, the id the hub gave. No other file changes here. Claude-Session: https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NZ's concept mapping now binds
nz_super_shares_residence_with_person_at_least_adult_age on Person with
{"kind": "coresident_adult", "min_age": 18} (R2C-20 H2). The edit is in
the frame adapter's nz.json; tools/build_nz_transport_graph.py copies that
file into N/concept_mapping.json. The 18 copies rulespec-nz core.yaml's
parameter nz_super_living_alone_adult_age (s 4A(3) and (4)) by hand,
because no RuleSpec formula reads it. The binding's note says so, and says
that the frame's household stands in for the statute's residence.
The closure entry moves from defaulted false to encoded by the concept
binding. concepts.encode@1 therefore stops writing the default
(column_kernels.py:407, input_closure.py:520-526) and writes the binding's
values (concept_mapping.py:2293-2318). The exempt-adults entry stays
defaulted false. Its reason now says that every single person who shares
a home with an adult gets the sharing rate, including one whose only
co-resident adults are exempt dependent children or temporary visitors.
The move made three statements false, and this commit corrects them:
reporting_classifiers.json's sharing disclosure and its method doc now
describe the understatement for that exempt group, and the graph's
pending_outputs no longer lists the NZS sharing binding (R2C-5).
The three NZ builders and refresh_concept_coverage.py regenerated
N/concept_mapping.json, export_replay_bindings.json's closure hash,
transport_graph.json's pending_outputs, and the axiom-nz coverage golden
and report (89 to 90 covered inputs). A load of the spec regenerated
tests/golden/nz_country_spec.json. Each builder's --check passes.
Pins: test_reporting_disclosure_cites_the_sharing_home_default_record
becomes ..._binding_record and asserts the encoded record, the exempt
reason and the new note. test_the_nz_roster_classes moves one input from
closure_default to derived (93 to 92, 322 to 323), as the H2 brief
allows; compose._support_roster classes the column derived, owned by
nz.encode.person, and its uncovered sources are the test's pending pair.
TestClosurePartition.test_the_committed_nz_surface_partitions_as_the_design_table
(a shared test) now fails on nz_super's partition (defaulted 11 to 10,
encoded 2 to 3). This commit leaves it unedited pending a hub ruling. 18
other shared tests (17 in test_calibration_columns.py and
test_packaged_precalibration_stages_pass_the_stage_guard[nz]) fail
identically at 891cb9f, before this binding.
Claude-Session: https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two tests run the real concepts.encode@1 kernel on NZS core.yaml with NZ's packaged concept_mapping.json and axiom_input_closure.json. Each test runs the kernel twice: once with the packaged resources (after H2), and once with a copy that drops the binding and restores the defaulted false entry (before H2). On the toy's nz.create frame, the twenty persons aged 65 or over each live with a partner aged 65 to 79 and a 16-year-old. Every one reads false before and true after. The toy has no superannuitant who lives alone or whose only co-resident is under 18, so a small frame covers those: alone (70) false, with a 40-year-old true, only with a 16- or 17-year-old false, with an 18-year-old true, and a person with no household id false. Before H2 every person in both frames reads false. Checks: both tests pass. A scratch script compared the binding with a direct read of household composition over all 120 toy persons and found no mismatch. Claude-Session: https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9cc37a5 moves nz_super_shares_residence_with_person_at_least_adult_age from defaulted to encoded in N/axiom_input_closure.json, so the nz_super binding's partition in TestClosurePartition.test_the_committed_nz_surface_partitions_as_the_design_table moves one root from defaulted to encoded: defaulted 11 to 10, encoded 2 to 3. The hub's ruling R2C-28 (#1183) clears this one line, at :810. The test's other assertions stand unchanged: 158 roots, and check == []. Checks: test_transport_evidence_kernels.py passes in full (156 tests). Ruff check and format --check pass on the file. Claude-Session: https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An independent review of H2 found that the sharing disclosure's evidence pointed only at the shares-residence record. 9cc37a5 left that record encoded, with no value or reason, while the disclosure's note blames the exempt-adults default. The evidence now cites both closure records, and the two NZ citation tests resolve both. The same review tightened the prose. The exempt-adults reason now names the sharing rate's source (Schedule 1 Part 1 clause 1(b)) instead of s 4A, says which person s 4A counts as living alone, and adds that S5's reg 17 superannuitant base rate reads the same weekly amount (as_rate_bridge.json:462-472). The binding note no longer says the mapping keeps min_age in step with the parameter, because nothing checks the two. The method doc's paragraph now opens with a sentence that stands alone. The transport builder copied the note into concept_mapping.json and wrote export_replay_bindings.json's new closure hash. Rendering the loaded spec's summary rewrote nz_country_spec.json. Each builder's --check passes. Checks: 12 test files that read the edited resources pass (2123 passed, 4 skipped for a missing feed or engine): test_nz_spec_package.py, test_country_spec.py, test_axiom_concept_mapping.py, test_nz_axiom_input_closure.py, test_spec_source_citations.py, test_concept_mapping.py, test_input_closure.py, test_transport_evidence_kernels.py, test_nz_transport_declarations.py, test_spec_engine_country_bundles.py, test_spec_only_country_packages.py and test_nz_transport_toy_graph.py. Ruff passes on the edited test file. Claude-Session: https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review 215 (#1183 comment 6616135497, nit 2) found that the small-frame test held one person with no household id, so it couldn't tell "a null id shares with nobody" from "null ids group together": mutant B (use_na_sentinel=False in _coresident_adult_flags) passed every H2 test. Person 11, aged 30, now has no household id either, and both 10 and 11 must still read false. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub reported H2 as conflicting with main once G landed. main and H2 share two merge bases, G's toy head 85d55af and main's 4a36edc, and a merge over either one alone conflicts in NZ files. Git's recursive merge over both is clean. This commit records that result: against main it changes exactly H2's own lines in H2's 15 files and nothing else, so the PR's diff doesn't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This PR's CI passed at 01:5xZ against main f8f543a. Main has since taken six PRs (#1345, #1493, #1467, #1426, #1465, #1391), 178 files, none of them this PR's. #1493 changes how the target-compilation kernels hash their code, so CI reruns here on today's main before the PR lands. The merge is clean over the single base f8f543a, and against main it changes only this PR's own lines, file for file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged by the NZ hub (session local_e407a527) at 10bcfd1 with --merge. Gates: |
H2 and #1501 landed on main, and both change test_nz_transport_toy_graph.py, as this PR does. git merges every other file without a conflict. The one conflict sits at the toy file's end, where H2's section and this PR's section both append. The resolution keeps main's file whole, #1501's edits included, then appends this PR's section unchanged: blob 5cb8c87. This PR's diff against main still adds the same 158 toy-file lines in the same order, and its other three files keep their patch-ids. No top-level name in the resolved file repeats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three PRs reached main after this PR's last CI run, which tested 17e2643 on main 0f0e777: #1506, #1485 (H2) and #1501. None of them changes a file this PR changes, and git merge-tree writes tree 79ee630 with no conflict. H2 adds an NZ coresident binding and teaches test_support's reference concepts the coresident_adult transform, which the concept-mapping differential beside this PR's encode change reads, so this merge has CI test the two together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1488) * Prove extension calibration nodes' measurement producers in compose The extension pass of compose_transport_graph compiled the extended graph and reran the ancestry guard, but never ran _check_measurement_proofs. So a calibration-tier scenario's calibrate.* node could calibrate against a money-scaled measurement that no proof covered: measurement_proofs reads only the declared graph's rows and never names those nodes. The extension pass now runs the check after the ancestry guard, as the skeleton pass does. For a calibration node that an extension adds, the compiled graph proves the producer under R2C-1's rule: exactly one compiled ancestor declares the measurement, and that ancestor outputs every proof column. R2C-15's only-writer rule applies as before. A skeleton calibration node keeps the declared comparison, which the pass now also runs against the extended graph. Tests: unit cases on the compiled toy; a differential property that the declared proof and the compiled extension proof agree, refusal for refusal, on chains of declarers and writers; and NZ's synthetic graph with four V3 mutants (M1, M2, M4 and the NZ hub's G7), each of which composes without this change. * Move the NZ-toy measurement-proof tests into NZ's toy-graph file The five tests that run the extension proof on NZ's synthetic graph name the toy's V3 nodes, and the NZ hub owns the toy and its node ids. They move, with their helpers, from test_transport_measurement_proof.py into test_nz_transport_toy_graph.py and use that file's module `toy` fixture, so a change to the toy edits only NZ's files. The shared file keeps the unit tests on the compiled toy and the differential property. The move adds G7 in the NZ hub's placement. V3's calibration keeps its base on V3, while preparation, the compiled targets and the problem move into the review version that rewrites both measured columns. The problem reads the rewritten columns, so only the calibration's artifact edge from the problem puts the rewrite among its compiled ancestors. The earlier G7 opens the calibration from the review version, where the rewrite joins its base, and doesn't need that edge. * Refuse a malformed measurement_id in the compiled proof check Review r2 found that the compiled check skipped a node whose measurement_id was neither None nor a nonempty string, while the declared check refuses it. With the string producer still in place, an extra upstream node declaring measurement_id 7 composed, though the same param on a declared row parks every measurement. r2 showed it on NZ's toy graph, where V3's earnings.cents with id 7 composed. _check_measurement_proofs now refuses such a node with the declared check's reason before it proves any producer, and reads an explicit None as no declaration, as measurement_proofs does. The new test gives a node upstream of calibration a measurement_id of 7, "" or None, and runs the declared check and the compiled check on a calibration node that an extension adds and on a skeleton one. Both checks refuse 7 and "" with the same reason, and both ignore None. Without the fix, the compiled check accepts the four 7 and "" cases, and those cases fail. The commit also fixes three docstrings that r2 flagged: the test module's account of the compiled tests, which patch in a proof; _chain's, whose nodes chain by step columns; and compose's module docstring, which now names gate_inputs, the other explicit way an extension may add a skeleton predecessor. * Say the compiled proof check scans malformed ids only under a proof _check_measurement_proofs returns before its malformed-id scan when the declaration proves no measurement, and its docstring now says so. The independent review of b64eb6c noted that the sentence read as unconditional. Two merges of main bring G (#1478) and then H2 (#1485) and #1501. The second keeps main's test_nz_transport_toy_graph.py whole and appends this PR's section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the merge base 875506e, main gained #1473, #1506, H2 (#1485), #1501, #1480 and #1488. The merge is clean. Two files changed on both sides, in separate hunks: compose.py (#1488 on main's side) and test_transport_evidence_kernels.py (H2 on main's side). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…losure (S2b PR 1) (#1482) * Add binding-local views to the Axiom adapter AxiomEngine takes an optional views sequence of AxiomView declarations: a logical engine entity no frame entity maps to (rulespec-be's Child), one bool person selector column, and an outside value per output. The adapter gathers the selected person rows in table order, runs the module's program on the logical entity, checks the engine's array against the selected count, and scatters it back through the same mask; every other row holds the outside value, cast through the output's graph dtype. An empty selection makes no engine call, and a null or non-bool selector refuses. View outputs resolve to the person entity, so callers that key on (binding entity, variable) read them unedited. Each view's program compiles under its own cache key, re-checks the pinned RuleSpec tree first, refuses dense relations, and contributes its derived metadata, so a Child-only module has metadata. The selector joins the person entity's relation_dependencies; view_root_inputs() reports each view's roots. axiom_engine_ref writes views only when present, so every existing reference is unchanged. The view code stays in axiom.py, because source_hash hashes defining files and does not follow imports. * Partition binding-local view roots in the input closure closure_partition no longer refuses a binding that declares views, and BINDING_LOCAL_VIEW_MESSAGE is gone. Per binding, each view's logical entity (Child) joins the live entities and maps to the person table, so a view's roots partition against the closure's Child entries and the existing frame check reads them on the person slice. Roots on that entity stay unmapped on every other binding, and unreached_roots still reads only the binding's engine entity, so it cannot excuse a view root. engine_root_inputs adds each view's roots under its logical entity through AxiomEngine.view_root_inputs(), and refuses a view binding on an injected adapter. binding_view_entities reads a binding's logical entities for the closure and compose. * Parse binding-local views in the registry and check them in compose The registry parses a binding's views block into AxiomView declarations (logical_entity, selector {column}, outside {name: value}) and builds the adapter with them. It refuses an empty list, views beside relations, a binding off the person entity, a logical entity that names a mapped engine entity, a repeated logical entity, a selector with any field but column, a structural selector, a selector that names a binding output, and an outside key that names no binding variable. Once the adapter is pinned, it checks each view's program for relations in place of the binding entity's program, requires each view's outside keys to equal the binding's variables on its logical entity, and refuses a view input named like a root input of the binding entity's program. _check_engine compares an injected adapter's views with the binding's and refuses views on an injected Python adapter. binding_view_dependencies reports each selector as a whole-table person slice, the same columns AxiomEngine.relation_dependencies reports to the rules kernels. Compose unions them with the relation dependencies for every rules node, refuses a view node whose person slice omits an input the closure encodes (other than through a graph node) or defaults on the view's logical entity, and requires each selector on the closure producer's person slice. * Pin NZ-shaped and relation engine references to goldens from the base No NZ CI test builds an axiom_engine_ref for NZ's rows: NZ's engine-free graph injects Python toy engines. The new golden holds references generated at main 4a36edc for NZ's six binding rows (unreached_roots included) over a synthetic tree, and for the registry and adapter relation fixtures, all built engine-free through build_transport_registry and axiom_engine_ref. The test compares them byte for byte, so a reference that gains "views": [] or "views": null without a declared view fails. A second test, gated on POPULACE_RULESPEC_NZ, builds NZ's six real references from the tree alone and compares them with a frozen copy the NZ hub writes at the base. * Add the changelog fragment for binding-local views * Test that every rules kernel requires a view's whole selector slice SimulateRulesKernel._require_relation_inputs reads the adapter's relation_dependencies, which now carry each view's selector, so a node whose person slice omits or masks the selector refuses before the engine runs. The test pins that path for a view adapter. * Pin view code to axiom.py and the no-view outputs to the base path The placement lint now walks every import node in axiom.py, relative and function-body imports included, and a second test requires AxiomView and every view helper to carry axiom.py's module name. A helper moved into a module axiom.py already imports (materialize, concept_mapping) would leave the simulate.* hashes unmoved by an edit to it; the module-name test refuses that move, which the import pin alone passes. P4 gains an output clause: with views=None, materialize returns the same names, order, dtypes and values, through the same engine calls, as a reference copy of main 4a36edc's no-view path kept in the test. NZ's CI graph injects toy engines and never runs AxiomEngine.materialize, so this is the executable guard on view-free outputs. * Class a view's defaulted roots as person columns in the support roster _support_roster mapped each defaulted closure entry to a frame entity through entity_names alone, so a defaulted Child root, which PR 2 writes on the person table, mapped to no entity and missed the closure_default class. _closure_defaults now maps a binding-local view's logical entity to the person entity for entries of that binding's module. Without views the mapping is unchanged. * Freeze NZ's real-tree engine references at the base test_nz_real_refs_match_their_frozen_copy compares NZ's six real references with a frozen copy, and wrote none without a rulespec-nz tree. This adds the copy, generated at PR 1's base (main 4a36edc) from a git archive of rulespec-nz 8dc2507d, the commit NZ's bindings pin. The base run froze the file, and a second base run passed against it. At this branch's head the test passes too, so the six references keep their bytes with views in place. The CI golden test passed at the base as well, which checks that axiom_engine_refs.json came from the base. * Document binding-local views in the declared-units guide The guide said binding-local views remained separate work. It now describes the declaration, the gather and scatter, the outside values, the registry's refusals, the selector slice, the closure's treatment of view roots, and the row-locality assumption that the skip-gated real-engine differential tests. * Union shared-path closure roots to remove binding-order dependence * Cover both logical views to catch dropped input roots * Validate complete outside declarations before pinning bindings * Preserve outside value types in view equality and hashing * Exercise view property edges across every graph dtype * Type and stabilize real Child differential inputs * Freeze CI reference bindings to isolate NZ edits * Clarify view evidence, serialization and selector refusals * Accept overrides of a view selector that a program reads The first review of the fix round (P2) found that 0d6201a's selector refusal also refused an override of a selector that a Person program reads as a root input. The registry accepts that shape, variables() lists the selector, and before 0d6201a the counterfactual bridge applied such an override. validate_input_override now names a selector only when no program reads it, which is exactly when the bridge refuses the override as outside the engine's inputs, so the check changes no override the bridge applies. test_a_selector_that_a_program_reads_stays_overridable drives the counterfactual bridge with a Person program that reads is_child: the override reaches that program's input and the rows the view selects. It fails on 0d6201a's check. The refusal test now also runs through the bridge, which calls no program. The guide states the rule. * Correct the two docstrings that review B flagged `_real_inputs` said the pinned engine refuses a wrong-typed input by name. At axiom-rules-engine 753ce7d, `src/dense.rs` reports a bool, text or date column as a type mismatch when a program needs a number from it ("expected decimal-compatible dense column" for an arithmetic operand, "{side} side of comparison is not numeric" for a comparison), and neither message names the input. The docstring now says that. `axiom_ref_goldens.py`'s module docstring wrote `"\n"` in a non-raw string, so it rendered a line break where the regeneration recipe needs the two characters. It now escapes the backslash. Two merges of main bring this branch up to date: `02265bb5` merges main `875506e8`, and `36800b14` merges main `3e7a7045` (#1473, #1506, H2 #1485, #1501, #1480 and #1488). git merged both without conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This is hub ruling R2C-20's H2 (tracking issue #1183). It targets main since G (#1478) landed as
f8f543a4. It started on #1483 (nzhub/g8r2c-g-toyat85d55af), with main4a36edcfmerged in (df9fce45) for #1477'scoresident_adultkind.1ad5bb17merges main again: main and the branch shared two merge bases (85d55af5and4a36edcf), so GitHub reported a conflict that git's recursive merge doesn't have. Against main,1ad5bb17changes exactly this PR's own lines in its 15 files.Before this PR, NZ's input closure defaulted
nz_super_shares_residence_with_person_at_least_adult_ageto false, so every single superannuitant got the s 4A living-alone rate. Now NZ's concept mapping binds it onPersonwith{"kind": "coresident_adult", "min_age": 18}: a person reads true when another person with the same household id is aged 18 or over.Review: brief 215 reviewed
1b56b916(#1183 comment 6616135497): APPROVE_WITH_NITS, Needs Max: no, no fixes owed. The hub took two of its nits:d82a2ba5gives the small-frame test a second null-id adult (nit 2), and the Mechanism note under "The toy, before and after" now gives the right reason F1 doesn't reach that test (nit 3). Its nit 4, main's redtest_the_batterys_binding_identity_adds_only_the_leaf_and_its_imports, came from #1342, and #1495 fixed it on main at 21:22Z 11/03. Brief 222 (#1183 comment 6616766311) reviewedd82a2ba5: APPROVE_WITH_NITS, no fixes owed. The toy file gave 33 passed, and mutant B turned the small-frame test red, with persons 10 and 11 reading true. For its one nit, the hub added person 11's row to the small-frame table below. G (#1478) landed asf8f543a4at 00:13Z 11/04. The hub retargeted this PR to main, and CI run 41745582220 tests it against main.Status: the hub's ruling R2C-28 (#1183) cleared the second shared count move,
TestClosurePartition's nz_super partition, and1725611capplies it. The 18 shared tests that failed in the run under Tests came from G's stack before its fix rounds: fix round 1 (20507ad8, R2C-26) fixes the stage-guard pin, and fix round 2 (f43e8fbb) the 17 calibration-column tests. Both rounds reached main with G, whose CI run 41729696579 passed every engine-free test; this PR's run 41745582220 tests the branch against main.Commits
891cb9f1adds the two hunks Belgium's hub prepared: theCoresidentAdultcase intest_support/microcosm_frame/concept_reference.py(23, 160-171) andcoresident_adultin the builder's bool set (tools/build_nz_transport_graph.py:208). Its stable patch-id,e3f8d1c790817d1c3e6677e68fd1f0a564f68af2, matches the hub's.9cc37a58adds the binding, the closure move, the regenerated files and NZ's moved pins, and corrects three statements the move made false.b78c8ad8adds the before-and-after tests, which run throughconcepts.encode@1.1725611capplies R2C-28:test_transport_evidence_kernels.py:810.1b56b916applies fixes from an independent review. The disclosure's evidence now also cites the exempt-adults default its note blames, and the commit tightens the binding note, the exempt reason and the method doc.The binding
packages/microcosm-frame/src/microcosm/frame/adapters/axiom_concept_mappings/nz.json:414-428: the new binding sits betweennz_super_person_ageandnz_super_single(the file orders by module, then by input name).tools/build_nz_transport_graph.pycopies it intoN/concept_mapping.json:414-428.nz_super_living_alone(rulespec-nzcore.yaml:550-573, s 4A). It says thatmin_age18 copies parameternz_super_living_alone_adult_age(core.yaml:65-79: 18 from 2026-04-01, source s 4A(3) and (4)) by hand, because no formula reads that parameter: the name appears once incore.yaml, at its definition. It says that the frame's household stands in for the statute's residence. It also lists the other differences: the binding counts a partner, but the partner gate (core.yaml:567,:664) means the rate never reads a partnered person's value; it counts adult dependent children and temporary visitors, whom the exempt-adults input carves out; ages are completed years at the survey date; and a person with no household id reads false.core.yamlat both pins (6fe181fcand8dc2507d). Both copies hash tod8ea0d15…, the hash the closure pins.The closure (
N/axiom_input_closure.json)"class": "encoded", "encoded_by": "concept_binding", withoutvalue,reason,citationornew_default.nz_super_all_shared_adults_are_exempt_dependent_children_or_temporary_visitorsstays defaulted false. Its reason now covers what that default does. Every person who has no partner and shares a home with an adult gets the sharing rate (nz_super_single_not_living_alone_weekly_rate_before_tax, Schedule 1 Part 1 clause 1(b): 595.57 against 647.37 living alone,core.yaml:80-111). That includes a person whose only co-resident adults are exempt dependent children or temporary visitors, whom s 4A counts as living alone, so the default understates NZS for that group. The reason adds that S5's reg 17 superannuitant base rate reads the same weekly amount (as_rate_bridge.json:462-472).Mechanism (read)
ConceptsEncodeKernel.run(T/column_kernels.py:396-417) passed the closure to_defaults(:212-232),InputClosure.defaults(F/input_closure.py:520-526) returned this input's false, and_defaultswrote it on every person throughapply_defaults(T/column_kernels.py:231).defaultsskips encoded entries, andmapping.encode(:403) dispatches to_coresident_adult_flags(F/concept_mapping.py:2232,:2293-2318). NZ's graph still declares thenz.encode.personoutput asbool, so the encoder's output dtype doesn't change.nz_super_ordinary_weekly_rate_before_taxpicks a single person's rate fromnz_super_living_alone(core.yaml:664-670). The pre-calibration stagenz.precal.nz_superoutputsnz_super_weekly_amount_before_taxas acalibration_measure(N/precalibration_stages.json:84-96), and S5's reg 17 base rate adds the same amount for a single-superannuitant unit (N/as_rate_bridge.json:462-472).Regenerated files
I ran
build_nz_transport_graph.py,build_nz_benefit_gross_up.py,build_nz_entitlement_graph.pyandrefresh_concept_coverage.py --engine axiom-nz, and each--checkpasses.N/concept_mapping.json:414-428: the binding.N/export_replay_bindings.json:3:axiom_input_closure_sha256moves from791c1cd4…tod2694200….N/transport_graph.json:4981:pending_outputsno longer lists the NZS sharing binding. This file holds one literal sha pin,concept_schema_sha256, which doesn't move; the graph reads the closure and mapping through resource selections.packages/microcosm-frame/tests/golden/concept-coverage/axiom-nz.jsonanddocs/concept-coverage/axiom-nz.md: covered inputs move from 89 to 90 and uncovered from 579 to 578.packages/microcosm-build/tests/golden/nz_country_spec.json(3, 46, 53, 59, 83, 94): the fingerprint (06ce7ca0…to9d0b57a1…) and five resource hashes. I regenerated it by rendering the loaded spec's summary, as G's commits did.Pins
test_nz_spec_package.py:3422-3447: I renamedtest_reporting_disclosure_cites_the_sharing_home_default_recordto..._binding_record. It resolves the disclosure's two citations: the encoded sharing record, and the exempt-adults default with its reason. It also checks the new note.test_keyed_citations_survive_a_record_inserted_before_the_target(:3238-3261) now expects both records forsharing_home.test_transport_evidence_kernels.py:2734-2735(TestSupportRoster.test_the_nz_roster_classes):closure_defaultmoves from 93 to 92 andderivedfrom 322 to 323, the move the brief clears.compose._support_rosterclasses the columnderived, owned bynz.encode.person, and its uncovered sources are the test's pending pair (household.rent,person.liquid_financial_assets).test_transport_evidence_kernels.py:810(TestClosurePartition, R2C-28): nz_superencodedmoves from 2 to 3 anddefaultedfrom 11 to 10. The test still counts 158 roots in total, withcheck == [].Beyond the brief
The move made three statements false, and this PR corrects them:
N/reporting_classifiers.json:212-213: the sharing disclosure said NZS was overstated because the input defaults false. It now says the model understates NZS for single people whose only co-resident adults are exempt, and that this moves their deciles downward. Its evidence cites both closure records: the sharing input's and the exempt-adults default's.docs/nz-reporting-classifiers-method.md:66-71: the same correction in the method doc.tools/build_nz_transport_graph.py:1271:pending_outputsdrops "and the NZS sharing binding (R2C-5)".The toy, before and after
The toy's adapters are pure Python stand-ins with invented rates, so this records the input only, not NZS amounts. I ran
concepts.encode@1on NZScore.yamlagainst the toy'snz.createframe: once with NZ's packaged mapping and closure (after), and once with a copy that drops the binding and restores the defaulted entry (before). All 120 persons read false before. After, 118 read true, and the two who read false are both under 65. A scratch script compared the binding with a direct read of household composition over all 120 persons and found no mismatch.Every toy superannuitant has a partner, so
nz_super_single(core.yaml:567,:664) keeps the rate from reading this input for any of them. The toy has no superannuitant who lives alone or whose only co-resident is under 18, sotest_h2_sharing_binding_on_the_household_shapes_the_toy_lacksbuilds a small frame:Person 10 has no household id (#1481, F1, changes how the frame treats such a person). The
coresident_adultkind factorizes household ids withuse_na_sentinel=True(F/concept_mapping.py:2305), so that person shares a household with nobody and reads false, as the default did. The test does reach_Context, the class F1 edits, throughmapping.encode. F1's change still doesn't reach it, because_coresident_adult_flagsreads the person table's own household ids (F/concept_mapping.py:2304-2306) and none of the three members F1 changes (_Context._household_rows,household_amount_for_persons,reference_rows); review 215 ran the cases at H2's head and with F1 merged, and they agree. Sinced82a2ba5, person 11 (30) has no household id either, and both read false, so the test also fails if null ids group together (review 215's mutant B,use_na_sentinel=False).test_h2_sharing_binding_turns_the_toy_superannuitants_default_into_truepins the toy rows, and the small-frame test pins every row above.Tests
I ran 42 files with
-n 4 --dist loadfile: the three the brief names,test_input_closure.py,test_concepts.py, the 16test_nz_*.pyfiles,test_fetch_nz_as_area_sources.py, and the 20 shared build test files where grep finds a read of NZ's packaged resources.9cc37a58.1b56b916: every file I edited after run1 started, plus the files that read the resources1b56b916edited (2123 passed, 4 skipped).Ruff
checkandformat --checkpass on every changed Python file.test_axiom_concept_mapping.pytest_calibration_columns.pytest_concept_mapping.pytest_concepts.pytest_country_spec.pytest_fetch_nz_as_area_sources.pytest_group_encode.pytest_input_closure.pytest_ledger_artifact.pytest_nz_as_area_crosswalk.pytest_nz_as_bridge.pytest_nz_axiom_input_closure.pytest_nz_benefit_gross_up.pytest_nz_earnings_bands.pytest_nz_family_reconciliation.pytest_nz_joint_geography.pytest_nz_money_normalization.pytest_nz_msd_benefit_rates.pytest_nz_msd_pre_activation_reconciliation.pytest_nz_reference_aging.pytest_nz_reference_intervals.pytest_nz_spec_package.pytest_nz_target_references.pytest_nz_transport_declarations.pytest_nz_transport_toy_graph.pytest_spec_engine_country_bundles.pytest_spec_engine_reemission.pytest_spec_only_country_packages.pytest_spec_source_citations.pytest_transport_column_kernels.pytest_transport_composed_graph.pytest_transport_declared_units.pytest_transport_evidence_kernels.pytest_transport_gate_bindings.pytest_transport_graph.pytest_transport_holdout_separation.pytest_transport_money_kernels.pytest_transport_population_kernels.pytest_transport_quantile_map_v2.pytest_transport_takeup_kernels.pytest_transport_target_kernels.pytest_transport_weight_quality.pyInherited (R2C-28), all failing the same way at
891cb9f1, before the binding:test_calibration_columns.py: G's714e10d0adds interval entries to NZ'scalibration_bindings.json. The tests callprepare_calibration_columns(tables, _bindings())(test file :38-44) with no interval document, socalibration_columns.py:106-107raises. The hub fixes it on G's stack.test_packaged_precalibration_stages_pass_the_stage_guard[nz]: main's pinned set (:277) lacks thenz_superstage that G's714e10d0adds. R2C-26 fixes it in E-G fix round: feed-free label tests, the stand-in rename and R2C-26 #1486 (20507ad8).Skips (89; each names an artifact, source directory or engine this environment lacks):
test_ledger_artifact.py(2): :613: "NZ_CHRONICLE_ARTIFACT is unset; real NZ Chronicle artifact unavailable"; :638: "NZ_CHRONICLE_ARTIFACT is unset"test_nz_as_area_crosswalk.py(3): :1085, :1096, :1105: "Pinned ArcGIS bytes are distributed separately; set NZ_AS_AREA_SOURCES to their directory to replay the crosswalk"test_nz_axiom_input_closure.py(2): :535, :610: "needs axiom_rules_engine with its dense extension and a rulespec-nz checkout at POPULACE_RULESPEC_NZ"test_nz_family_reconciliation.py(9): :264 x2, :332 x2, :352, :376 x2, :485, :563: "NZ_CHRONICLE_ARTIFACT is unset; public NZ feed audit unavailable"test_nz_joint_geography.py(12): :394: "Set NZ_CHRONICLE_ARTIFACT to replay the pinned published TA ERP facts"; :592, :602, :635 x7, :654, :671: "Set NZ_AS_AREA_SOURCES and NZ_JOINT_GEOGRAPHY_SOURCES for pinned-source replay"test_nz_msd_pre_activation_reconciliation.py(1): :121: "NZ_CHRONICLE_ARTIFACT supplies the staged pinned feed audit"test_nz_reference_aging.py(16): :88, :137 x10, :167, :210, :225, :261, :307: "NZ_CHRONICLE_ARTIFACT is not set"test_nz_reference_intervals.py(1): :435: "NZ_CHRONICLE_ARTIFACT is unset; public NZ feed audit unavailable"test_nz_target_references.py(41): :355, :463, :583 x2, :835, :846 x2, :865, :900, :917, :960 x17, :988, :1040, :1070, :1099, :1137 x6, :1158, :1178, :1228, :1311: "NZ_CHRONICLE_ARTIFACT is unset; public NZ feed audit unavailable"test_nz_transport_declarations.py(2): :290 x2: "Pinned feed not staged"https://claude.ai/code/session_01WR8LHbmJT4MutJib6vfq8v
🤖 Generated with Claude Code