Repository navigation
G8-r2c D: per-band support diagnostics and the measurement_prefit gate - #1457
Merged
Merged
Conversation
scale_money_and_intervals now accepts a summed money row: an identity
row with no aggregate_fact_key, a selector that pins a dimension to a
list of members, and an expected_member_count equal to the cells those
lists span. The resolver emits one operand {dimension_values,
expected_member_count, weight}; the ledger's multi-member
linear_combination path sums it. The count companion keeps value
operation sum over the same members, because the ledger's identity
path resolves one fact. The resolver refuses a wrong member count, a
non-identity money row, count and money member sets that differ, and
members two rows share. The receipt records each factor's binary64
weight and its exact relative rounding (R2C-10).
compose.measurement_proofs satisfies measurement:<id> only when, for
every calibration node, exactly one upstream node declares
measurement_id: <id> and writes the scaled table's income and universe
columns on its entity (R2C-1). Upstream follows the graph compiler's
predecessor rule over the declared rows. reference_profile_view passes
the proofs to the resolver; a proven row becomes active, any other row
stays parked, and its activation gap names the measurement and the
reason.
Invariants: a compiled summed amount equals the factor times the sum
of its members, and the count equals the sum of its members; a row is
active exactly when its measurement is proven. The tests in
test_reference_summed_bands.py and test_transport_measurement_proof.py
cover them, but this commit is pushed before the environment finished
installing, so they have not run yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP1LrUqzw1Kaau8bvP
An interval table that declares diagnostics.support_household_entity reports, per band, the distinct source households and the source-household Kish ESS of its count and amount contributions. Contributions are summed per source household before the Kish ratio, so donor clones add no support. A unit reaches its household through the person table's membership columns; a unit with members in several households, or an undeclared household entity, refuses by name. A table without the field parses, classifies and reports as before. The measurement_prefit binding applies R2C-3's four checks to the problem's matrix at the entering weights: roster totals, concentrated discrepancy (a band counts once), per-band support for counts and amounts separately, and missing evidence. Every threshold comes from the gates.json entry. The frozen country_spec allow-list doesn't yet admit the gate name; a hub ruling is pending on #1183. Invariants: ESS never exceeds distinct source households, and merging clones leaves it unchanged; weighted band counts equal the weighted indicator sums. This commit is pushed before the environment finished installing, so its tests have not run yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2tyHP1LrUqzw1Kaau8bvP
Merge nzhub/g8w4d-nz-wiring at fcdb473 (the w4 fix round's hand-off) into G8-r2c hunk A. Git merged without conflict: the fix round's edits to T/compose.py and T/gate_bindings.py sit apart from A's hunks. git diff fcdb473 HEAD shows A's five files and nothing else, and the removed and added lines in compose.py match A's own diff against d81280b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
Merge nzhub/g8r2c-a-summed-bands at 923b4de, which carries w4d fcdb473, into G8-r2c hunk D. Git merged without conflict. git diff 923b4de HEAD shows D's five files and nothing else, and the lines gate_bindings.py adds match D's own diff against A's old head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
…raph The measurement proof (R2C-1) now reads the id a producer declares in its params, under measurement_id, as a literal string: measure.combine@1 takes it as context.params["measurement_id"]. The node-level measurement_id field is gone from the transport node schema, and a non-string value proves nothing. Upstream of calibration now means ancestry under the S5 guard's declared rule (population or base, artifact producers and the writers of each read), factored out of _validate_precalibration_ancestry and shared. Composition then rechecks every proof against the compiled skeleton's exact predecessors and refuses a proof the compiled graph contradicts. The summed-band fixture now carries the dimension and target labels the ledger requires for a multi-fact target, so the compile tests run the real ledger. Invariant: a measurement is proven exactly when each calibration node has one declaring ancestor that writes the table's columns on its entity. Checked by test_transport_measurement_proof.py (16 passed) and test_reference_summed_bands.py (18 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
Hunk A's resolution validator refuses a factor companion that is active without its measurement's proof. Two fixtures forged activation after resolution: factor_transport_fixture(activate=True) and the CLI replay test's measured view. Both now hand the resolver a synthetic proof, so the real resolver activates the rows and binds the proof into the selection identity. The proof reads the raw transport_graph resource, as the rest of the reference-profile view does, so a spec whose graph composition would refuse for another reason still resolves its references. Invariant: a factor row is active exactly when its receipt carries the measurement's proof. Checked by test_transport_target_kernels.py, test_nz_money_normalization.py, test_transport_run_entry_factors.py and the CLI replay test (200 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
A mutant that removed the comparison between a summed money row's members and its count companion's members left every test green. The comparison cannot fire: the companion key includes the canonical member lists, so a count row pairs with a money row only over the same member set, and a mismatch already refuses as a missing companion. The refusal now says so. Invariant: summed money and count rows pair only over one member set. Checked by test_summed_rows_refuse_each_inconsistency[mismatched_members] (18 passed in test_reference_summed_bands.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
Carry A's tested head 4d2f3b2 (params-based measurement proof, compiled recheck, fixture proofs, member-set simplification) into D. One conflict, in test_support/microcosm_build/reference_intervals.py: A reformatted the end of the summed-band fixtures, and D appends the pre-fit fixtures there. The resolution keeps both sides' lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
Add "measurement_prefit" to ALLOWED_GATE_FUNCTIONS in country_spec.py, the one line of that file ruling R2C-12 lets hunk D touch. The pinned test of the loader's refusal goes, and a test loads a gates.json entry naming the gate with R2C-3's numbers. D's pushed tests had not run. They now build the gates.json header the loader requires (version, country, policy, phases), and the two-household tax-unit test gives the frame explicit unit weights, so the frame's own weight derivation does not refuse before the support diagnostics do. Invariant: a gates.json entry naming measurement_prefit loads with its parameters intact. Checked by test_transport_measurement_prefit.py (31 passed) and test_reference_interval_support.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
The pushed clone property split each weight by the clone count and skipped its assertion whenever the split was inexact in binary64, which is most draws for 3 clones. It now gives each original record clones x w and each clone w, so the per-household sums are exact and the assertion runs on every example. It also checks that a band with no source household reports an ESS of 0, and an example pins the empty-band report. Invariants: source-household Kish ESS <= distinct source households, and cloning records within a source household leaves per-band support unchanged. Checked by test_reference_interval_support.py (11 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
The docstrings now name what the code does: membership runs through the person table's group columns, a unit must nest in one household as the frame's group-to-group placement requires, and an amount contribution's weight is w x z at the weights entering calibration. No behaviour changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
A mutant that lowered the source-household floor to 29 left every test green: the 29-household case also carried an ESS of 29, so the ESS check refused it alone. The test now names the expected refusal lines, and a case with 29 households and an ESS of 30 refuses on the household floor alone. The mutant now turns four cases red. Invariant: the gate refuses a band with fewer than 30 source households, or a Kish ESS below 30, for count and amount separately, and exactly 30 passes. Checked by test_transport_measurement_prefit.py (33 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
The closed list at D found two pins. The registry roster test lists every bound gate, so it gains measurement_prefit. The gate-bindings module holds no numeric literal beyond 0 and 1 (test_holds_no_threshold_literal), and _ratio_bounds compared a length with 2; it now checks for a pair by unpacking. A new test refuses empty, short, long, string and reversed bounds. Invariant: gate_bindings.py carries no threshold literal, and every registered transport gate is on the roster. Checked by test_transport_gate_bindings.py, test_transport_measurement_prefit.py, test_transport_binding_identity.py and test_transport_graph.py (216 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6HQomkwKHCkdAgY9cy
This was referenced Nov 2, 2026
Ruling R2C-15: a measurement holds only when its declaring producer is the only ancestor of each calibration node that writes each of the interval table's income and universe columns on the table's entity. The review's item 2c built a graph where an undeclared later-version rewrite of the income column passed both checks while the receipt named the declarer. Before this change measurement_proofs proved it, the compiled check accepted it, and compose_transport_graph composed the composed toy with the rewrite grafted on. measurement_proofs now collects every node in the calibration node's declared closure whose outputs hold each required column, and parks the rows unless that list is exactly the producer. _check_measurement_proofs applies the same rule over the compiled ancestors and the composed nodes' outputs and raises. Both share _second_writer_refusal, which names the measurement, the calibration node, the column and every writer. The rule has no ordering logic: a writer before or after the producer refuses, with or without rewrite. Checked with new pins in test_transport_measurement_proof.py: item 2c, a writer before the producer, a universe-only rewrite, a rewrite-free second writer, the composed toy, a compiled-only case, two controls that still prove, and a Hypothesis property over declared chains. Dropping the declared check turns 7 tests red; dropping the compiled check turns test_the_compiled_check_alone_refuses_a_second_writer red. measurement_proofs and validate_transport_activation give identical outcomes on every packaged spec (am, be, nz, uk, us) before and after. The same commit adds test_a_calibration_endpoint_without_a_calibrate_kernel_is_a_root for the review's OA2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6JfFhwRFMYkokanqow
The review's OA1 and OA3 mutants left every test green. Two new pins in test_reference_summed_bands.py now kill them. OA1: test_a_tampered_rounding_record_refuses changes one field of the receipt's factor_weight_rounding record (weight, exact numerator or relative rounding), recomputes the receipt hash so that only the rounding recomputation can see it, and expects validate_reference_resolution to refuse. Deleting that recomputation turns all three cases red. OA3: test_summed_rows_refuse_each_inconsistency gains a mapping member and a list member inside a summed row's member list. Dropping the scalar check in _summed_members turns both cases red. OA2's pin landed with A1 (05bb18c): dropping the endpoint clause of _calibration_roots turns test_a_calibration_endpoint_without_a_calibrate_kernel_is_a_root red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6JfFhwRFMYkokanqow
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6JfFhwRFMYkokanqow
The review's OD1, OD2 and OD3 mutants left every D test green. Each now has a test that kills it. OD2: test_three_discrepant_bands_refuse_and_two_pass takes a role, so two, three and four bands fail on the amount only as well as on the count only. Three or more refuse under the concentrated rule, and the failure line names the count. Making only the count role mark a band discrepant turns the six amount cases red. OD3: the test _Problem multiplies a two-column matrix by the weights it receives, with non-unit initial weights (2, 4). The matrix gives the declared model exactly at w0 and 5/12 of it at unit weights. test_the_model_side_is_the_matrix_at_the_initial_weights asserts w0 is not all ones and that the gate's band model is A·w0. Evaluating the comparator at unit weights turns that test and the edge cases red. OD1: test_a_zero_weight_household_is_not_counted adds a household of weight 0 to the hand-computed ESS case; band_support still reports two source households and the same ESS. Dropping the value != 0 filter in _support_measure turns it red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6JfFhwRFMYkokanqow
R2C-15 makes the producer the only ancestor of a calibration node that writes each measured column. Both checks collected writers from node outputs, so an EXPAND whose `params.expand_cells` names the income passed `measurement_proofs`, `_check_measurement_proofs` and `compose_transport_graph`, while the S5 reader in the same file counts those cells as writes. The declared check now asks `_declared_writer`. It also counts an EXPAND's literal cells, and it treats an EXPAND whose cells come from a resource as writing every column, as `_precalibration_declared_reads` does. The compiled check reads the cells through `_precalibration_expansions`. No packaged spec declares an EXPAND, so no packaged outcome changes. The invariant: a measurement proves only when no ancestor other than its producer may write a measured column, by output or by EXPAND cell. `test_transport_measurement_proof.py` gives 33 passed. Dropping the resource clause fails `[expand_resource_cells]` alone, and `test_packaged_spec_refuses_with_every_activation_gap_named` still passes. The patch comes from Belgium's hub, patch-id a48429b46f51315760a66b27bdf60daf2c342bba. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review 200 measured the property reaching R2C-15's branch in 16 of 20 seeded runs, about three examples a run, and the proof about once a run. Three explicit examples now cover one declarer alone and a second writer after it and before it. With _second_writer_refusal never refusing, both second-writer examples fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Columns model/4 and model/8 at unit weights give 3/8 of the model, not 5/12. No test asserts the fraction. Review 200 found this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Merged by the NZ hub (session local_e407a527) at 91bf4c0 with --merge. Gates: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
G8-r2c shared hunk D (tracking #1183): per-band support diagnostics and a release-blocking
measurement_prefitgate (ruling R2C-3), with ruling R2C-12's line incountry_spec.py. Stacked on hunk A (#1456,nzhub/g8r2c-a-summed-bands); the two share one re-key window.B/ispackages/microcosm-build/src/microcosm/build/,T/isB/transport/,F/ispackages/microcosm-frame/src/microcosm/frame/, and tests are inpackages/microcosm-build/tests/engine_free/shared/.af038d04merges A's EXPAND writer fixd916ea8dinto D, which movesT/compose.pylines after:591down by up to 17.91bf4c0fthen merges A'sbf418af1, test lines only, and changes one number in a test docstring. Other lines are at4c644db7, after the fix round. The round changed nosrcline in D, so everysrcline number below still holds.The change
Per-band support (
B/reference_intervals.py). A table that declares the optionaldiagnostics.support_household_entitygets, per band, the distinct source households and the source-household Kish ESS, for count and amount contributions separately (band_support,:1027;kish_ess,:1010).wto its band's count andw × zto its band's amount. Herewis the record's weight as the diagnostics read it, the frame's weights on the table's entity, which are the weights entering calibration, andzis its floored amount in cents. So an amount contribution's weight isw × z, the record's share of the band's weighted amount.record_source_households,:1077). The frame declares membership only through the person table: each group entity has a person columnEntitySchema.membership_column(group)(F/schema.py:138), whichFrame._group_positionsreads (F/bundle.py:649). A person reads its household column directly. A unit on another entity, such as atax_unit, reads its members' households and must nest in exactly one, asFrame._aggregate_group_to_grouprequires when it places one group onto another (F/bundle.py:1070). So two tax units in one household count as one source household. The code counts source households, not units.household.source_household_id.boundary_diagnosticsrefuses source households for such a table, and its report carries nosupportkey (:801,:933-945,:995).The gate (
T/gate_bindings.py:1664,_measurement_prefit; binding at:2044). It reads theproblemartifact and the battery's reservedgraph_evidencemap. Every threshold comes from the country'sgates.jsonentry, and none has a default. A country without the entry gets no gate, because the battery evaluates only the entries its manifest lists (B/gate_battery.py:705). The gate refuses on R2C-3's four checks:Roster totals (
:1803). The gate sums the model total over every band's count row, and separately over every band's amount row, across all the entry's declared tables together (NZ declares one table, with 12 bands), and compares each sum with the summed reference. The model total is the problem'sA @ w0at the weights entering calibration (problem.estimates(initial_weights),:1726), and the reference is the matrix's right-hand side, the aged reference the matrix uses (target_vector,:1727). It refuses outsideroster_total_bounds, inclusive: with R2C-3's [0.75, 1.25], exactly 75% and 125% pass.Concentrated discrepancy (
:1809). A band is discrepant if its count or its amount falls outsideband_bounds, and it counts once if both fail. The gate refuses when more thanmaximum_discrepant_bandsbands are discrepant. R2C-3's "3 or more refuse" ismaximum_discrepant_bands: 2.Support (
:1777,:1782). It refuses a band with fewer thanminimum_source_householdssource households, or a Kish ESS belowminimum_kish_ess, checked for count and amount separately. Exactly 30 passes.Missing evidence (
:1714onward). Today the gate checks:required_evidenceis wired into the battery as an artifact edge (graph_evidence);So of R2C-3's evidence items, the native-band report, the omission register, the VSI, VEX and VPT runs and the identity receipts are checked for presence only, by the alias the country declares. The gate doesn't read their contents.
R2C-12.
B/country_spec.py:134adds"measurement_prefit"toALLOWED_GATE_FUNCTIONS. That's the only line of that file this PR touches. The test that pinned the loader's refusal is gone, andtest_a_gates_json_entry_naming_the_gate_loadsloads agates.jsonentry naming the gate.R2C-3's numbers are pinned in
test_support/microcosm_build/reference_intervals.pyasR2C3_PREFIT_THRESHOLDS, andtest_r2c3_thresholds_are_pinnedpins them for the NZ session to reuse. NZ's own entry belongs to the NZ session.Invariants
test_ess_is_at_most_distinct_households_and_clones_leave_it_unchanged: 7 source households over a nil band and 3 positive bands, so it reaches shared households and empty bandstest_weighted_band_counts_equal_weighted_indicator_sums, againstinterval_indicatorstest_two_tax_units_in_one_household_count_once_with_source_household_ess,test_clone_households_share_one_source_householdtest_a_zero_weight_household_is_not_countedtest_the_model_side_is_the_matrix_at_the_initial_weights; the test problem'sestimatesmultiplies a matrix by the weights it receives, with w₀ = (2, 4)test_a_table_without_support_fields_parses_and_reports_as_beforetest_roster_totals_refuse_outside_75_to_125_percent,test_three_discrepant_bands_refuse_and_two_pass(count-only and amount-only failures, 2, 3 and 4 bands),test_band_bounds_are_inclusive_at_50_and_200_percent,test_a_band_failing_count_and_amount_counts_once,test_support_refuses_below_30_households_or_ess_separately,test_missing_evidence_refuses_each_wayMutants
winstead ofw × ztest_two_tax_units_in_one_household_count_once_with_source_household_esstest_an_entity_without_household_membership_refuses_by_name[frame0-None-None]test_a_table_without_support_fields_parses_and_reports_as_beforetest_support_refuses_below_30_households_or_ess_separately. This mutant survived the pushed test, whose 29-household case also had ESS 29; the test now checks which check refused.<=instead of<>=instead of>test_a_band_failing_count_and_amount_counts_oncetest_band_bounds_are_inclusive_at_50_and_200_percent[0.5-True]test_missing_evidence_refuses_each_waytest_a_gates_json_entry_naming_the_gate_loadstest_a_zero_weight_household_is_not_countedtest_three_discrepant_bands_refuse_and_two_passtest_the_model_side_is_the_matrix_at_the_initial_weightsand the edge cases that pass at w₀At
4c644db7I reran two of the mutants above: no grouping (5 tests red, among themtest_two_tax_units_in_one_household_count_once_with_source_household_essand the clone property) and concentrated>=(test_a_band_failing_count_and_amount_counts_onceand the four 2-band pass cases).Merge-up
7bc34fdbmerges A's merge-up923b4de1, which carries w4dfcdb47395293955347ec2697feef1a736a76cc23, into D. Git merged without conflict.git diff 923b4de1 7bc34fdbshows D's five files and nothing else, and the lines D adds toT/gate_bindings.pymatch D's own diff against A's old head.38cf1bf0merges A's tested head4d2f3b2binto D. One conflict, intest_support/microcosm_build/reference_intervals.py: A reformatted the end of its fixtures and D appends there. I kept both sides.5e4c846amerges the fix round's A head30ae4599into D with--no-ff. Git merged without conflict, and the merge brings in only A's fix-round files.af038d04merges A's EXPAND writer fixd916ea8dinto D with--no-ff. Git merged without conflict, andgit diff 4c644db7 af038d04shows only that fix's two files.40079dfamerges A'sbf418af1into D with--no-ff, and91bf4c0fcorrects the fake's docstring: columns model/4 and model/8 give 3/8 of the model at unit weights, not 5/12. The delta review (Activate the NZ graph against pinned Chronicle facts (NZ graph G8) #1183 comment 6592283997) asked for both.git diff 4c644db7 91bf4c0fshows the EXPAND fix's two files plus that docstring's test file, and the proof and prefit test files give 80 passed at91bf4c0f.nzhub/g8w4d-nz-wiringis still atfcdb4739.Shared hunks
Against A's head
30ae4599, at4c644db7:B/country_spec.py: 134B/reference_intervals.py: 49, 59, 67-68, 134-138, 171, 331-334, 336-338, 446, 782, 784-790, 801-805, 933-943, 945, 995-1134, 1156-1160T/gate_bindings.py: 46-49, 1636-1835, 1900-1905, 2044-2052tests/test_reference_interval_support.py: 1-386tests/test_transport_gate_bindings.py: 605tests/test_transport_measurement_prefit.py: 1-300test_support/microcosm_build/reference_intervals.py: 380-423The fix round changed only D's two test files. The
srcranges are as they were at38ae4a74.The pin in
test_transport_gate_bindings.py:605gainsmeasurement_prefit, because the roster test lists every registered gate. No module-hash pin moved.Implementation hashes
I computed
implementation_hash()for 43 transport kernels (every kernel the composed toy registry installs plus every module-level transport kernel), 89 UK runtime kernels (the UK spine registry and every UK kernel object or no-argument kernel class) and 6 US runtime kernels (the post-transfer registry on a stub engine and the module-level kernels), each in its own head's tree. While each hash ran, a trace recorded every source file it read. No UK or US kernel readsreference_profiles.py,reference_intervals.pyorcountry_spec.py, and none moves. That includes the UK target kernels (uk.full.target_compilation@1,uk.full.national_targets@1and the rest), which leavecountry_specout on purpose (uk_runtime/target_compile_modules.py:59,EXCLUDED_DIRECT_IMPORTS). EditingT/compose.pymoves nosource_hash: no kernel reads it. EditingT/gate_bindings.pymovesgates.battery@1, whoseimplementation_hashfolds in its bindings' sources (T/gate_kernels.py:747-775), so D's additions there move the battery too.fcdb4739calibration.prepare@1reference_intervals,reference_profilesdiagnostics.calibration@1reference_profilesgates.battery@1country_spec,gate_bindings,reference_profilesgeography.support_from_facts@1reference_profilestakeup.assign@1reference_profilestakeup.compare@1country_spec,reference_profilestakeup.gap@1country_spec,reference_profilestargets.compile@1country_spec,reference_profilestargets.problem@1reference_intervals,reference_profilestransport.create@1country_spec,reference_profilestransport.create@2country_spec,reference_profilestransport.quantile_map@1country_spec,reference_profilesThe fix round moves no hash. I recomputed
implementation_hash()at38ae4a74and at4c644db7, each in its own worktree: 43 transport kernels (the composed toy registry plus module-level transport kernels), 37 UK and 3 US runtime kernels that construct without an engine. All 83 hashes match, as do A's at4d2f3b2band30ae4599.uk.full.rotated-holdout@1andus.post_transfer.derive@1need an engine to hash, so this run skipped them.D adds no kernel to A's set. It moves
calibration.prepare@1andtargets.problem@1again throughreference_intervals, and R2C-12's line moves the seven kernels that hashcountry_specagain:targets.compile@1,takeup.compare@1,takeup.gap@1(not in R2C-12's list),transport.create@1/@2,transport.quantile_map@1andgates.battery@1. D's additions toT/gate_bindings.pyalso move the battery. The battery's binding identity (the SHA-256 ofrepr(GateBatteryKernel(TRANSPORT_GATE_REGISTRY)._identity[0]), first 16 hex digits) moves at D only, from715e6202808aade7atfcdb4739and A to55947149ba78ec86, because the registry gains themeasurement_prefitbinding.Tests
The closed list at
4c644db7(one pytest run,-n 4, no--maxfail): 2714 passed, 84 skipped, 0 failed. Per file: country_spec 143, nz_as_area_crosswalk 67 (3 skipped), nz_as_bridge 34, nz_axiom_input_closure 19 (2 skipped), nz_benefit_gross_up 16, nz_family_reconciliation 7 (9 skipped), nz_joint_geography 21 (12 skipped), nz_money_normalization 56, nz_msd_benefit_rates 6, nz_msd_pre_activation_reconciliation 4 (1 skipped), nz_reference_aging 6 (16 skipped), nz_reference_intervals 13 (1 skipped), nz_spec_package 1203, nz_target_references 16 (38 skipped), nz_transport_declarations 29 (2 skipped), nz_transport_toy_graph 23, reference_interval_support 12, reference_intervals 107, reference_profiles 129, reference_summed_bands 23, spec_engine_country_bundles 22, spec_only_country_packages 37, transport_composed_graph 469, transport_gate_bindings 131, transport_graph 15, transport_measurement_prefit 47, transport_measurement_proof 27, transport_population_kernels 32. The repo has notest_be_*.pyfile.Every skip needs data or an engine this environment doesn't have:
nz_axiom_input_closure2 (needsaxiom_rules_enginewith its dense extension and a rulespec-nz checkout);nz_transport_declarations2 (pinned feed not staged);nz_as_area_crosswalk3 (needsNZ_AS_AREA_SOURCES);nz_joint_geography11 (needsNZ_AS_AREA_SOURCESandNZ_JOINT_GEOGRAPHY_SOURCES);nz_target_references38,nz_reference_aging16,nz_family_reconciliation9,nz_joint_geography1,nz_reference_intervals1 andnz_msd_pre_activation_reconciliation1 (needNZ_CHRONICLE_ARTIFACT).Checks:
python3 tools/ci_test_plan.py verifyprintsverification=okat4c644db7, anduv run ruff check packages/microcosm-buildpasses. I didn't rerun the hash pins outside the closed list or the NZ builders'--check. The round changed no file they read, and the earlier run at38ae4a74stands for them.Fix round
The independent review (#1183, comment 6589309875) drove this round. Lines are at
4c644db7. No fix touched sharedsrc.5e4c846aT/compose.pyand tests4c644db7tests/test_transport_measurement_prefit.py:157-174(test_three_discrepant_bands_refuse_and_two_pass), againstT/gate_bindings.py:1768-1769_Problem.estimatesuses its weights; w₀ = (2, 4)4c644db7tests/test_transport_measurement_prefit.py:40-62,:124-134(test_the_model_side_is_the_matrix_at_the_initial_weights), againstT/gate_bindings.py:1726test_the_model_side_is_the_matrix_at_the_initial_weightsand the edge cases4c644db7tests/test_reference_interval_support.py:190-206(test_a_zero_weight_household_is_not_counted), againstB/reference_intervals.py:1019test_a_zero_weight_household_is_not_countedNot done
gates.jsonentry, its evidence aliases and the household entity its coarse table declares belong to hunk G. The problem frame must carry the household entity andsource_household_idfor the table's support to run; hunk G needs to wire that.https://claude.ai/code/session_01LwQq6JfFhwRFMYkokanqow
Generated by Claude Code