Consolidate the US survey graph build and enrichment pipeline - #893
Draft
MaxGhenis wants to merge 94 commits into
Draft
Consolidate the US survey graph build and enrichment pipeline#893MaxGhenis wants to merge 94 commits into
MaxGhenis wants to merge 94 commits into
Conversation
…m-consolidated-main-20260909
This was referenced Sep 9, 2026
Draft
…ntegration-20260909
Integrate the PUF59 profile, source-specific growth, receipt-bound donor codec, and current survey financial and demographic source owners. Fix checkpoint test coverage and pool registry import order, and document block-first US and small-area UK geography as pending build requirements.
This was referenced Sep 10, 2026
…int tests The restoration rebuilds the Frame with the input's mass log and metadata so a v4 checkpoint keeps its weight history (regression parametrized over an empty log, a declared mass change, and metadata forwarding). The two ASEC source test modules that test_us_asec_coverage_authentication imports were never committed; CI failed on their absence. The helper is classified as bounded I/O with no population treatment in the runtime inventory.
The strict-leaf audit stub now mirrors PolicyEngineUSVariableMetadataIndex's keyword-only surface (include_consumers), and the PUF55 recipient graph is 19 run nodes plus the projection and matrix nodes since geography moved after the initial clone.
…ary check The US source runtimes require microunit (microcosm-frame's us extra); an installed constellation without it fails closed instead of skipping, which is a false packaging verdict. The extra is requested against the built wheel so the gate proves the wheel carries resolvable extra metadata; policyengine-us stays absent.
…ess inventory The consolidation added 113 graph-native source, catalogue, composition, clone, geography, predictor, PUF and calibration modules that the reviewed inventory did not list, so the classification tests failed closed. Each is recorded from its module docstring (reviewed 2026-09-11) in a separate set; none is a migrated population-treatment registry operator, and the inventory does not exempt any of them from the source-spine scan. The pool build tool's import graph is re-pinned from 70 to 73 modules: it now also reaches _person_signal_summary, operator_column_contracts and reported_coverage_source, which join the required set so a silent drop fails.
… integration Three attested kernel modules changed on this branch — acs_transfer (its derived-transfer column contract moved to operator_column_contracts), housing_inputs (ACS rent donor/recipient preparation and finalization) and microcosm.fit.qrf (predict_from_uniforms draw path) — so the seed protocol's content digest and the compiled seed stream map digest legitimately moved. Main is fully merged (6f7571e); spec_engine and the US spec are unchanged. The committed F0 coverage report is regenerated from the re-pinned inventory and tools/spec_engine_coverage.py --check passes.
…s guard The composed two-survey population is source-qualified by charter: ACS and ASEC rows are composed, cloned and bound to their original records, and the PUF55 routes condition on source-owned Social Security totals (known for ASEC reporters, unknown otherwise). Seventeen launch modules that read the support-channel and spine-source-id columns to select or validate origin rows join the reviewed provenance owners with their reason; none routes a PUF-detail imputation by spine. Twenty-one further modules trip only the scanner's fail-closed dynamic selector class. Every site was read: they index tuples and state records, node/kernel maps keyed by node id, byte payloads in codecs, PUF donor tables by profile-declared names, or numpy masks -- never a provenance column. A separate reviewed set accepts that class for exactly those modules while accessor calls and contraband source columns keep failing, with a regression that proves the acceptance never hides either.
The derive phase imports policyengine-us, which reads eight public parameter CSVs at module import; the boundary refused them as data files, and the shared engine lane's diagnostic failed on every run of this branch. The eight files are now pinned by relative path, size and RECORD sha256 (verified against 1.819.0), hashed once before the hook is armed, and only their resolved paths may be opened; any other data-suffixed open, a pin whose bytes or RECORD differ, or a pinned path outside the distribution still refuses. The report records the policy and pins. Two import-time side effects of the same chain are recognised narrowly: psutil reads /proc/stat for its CPU-times layout (metadata, like /proc/cpuinfo), and urllib3 probes IPv6 by creating one socket and binding it to loopback from _has_ipv6 -- only socket.__new__ and a loopback bind issued from that function are accepted; connect, resolve, send and every other socket event keep the NETWORK_OR_CHILD refusal.
The pinned roster omitted microcosm.calibrate.gates and microcosm.calibrate.initialization, which the seed protocol's direct-kernel attestation has listed since before this branch (spec_engine/seeds.py is unchanged against main), so the derive phase refused SEED_MODULE_ROSTER on every run once the boundary let the engine import through. Both modules and their source files join the roster and the stamped source paths; the loaded US bundle's 44-module roster now matches the pin.
The seed protocol's content digest moved on this branch (three attested kernel modules changed: acs_transfer, housing_inputs, fit.qrf) and every country bundle's spec_sha256 and the loader's golden semantic-hash vector embed it, so these four pins move with the seed re-pin already recorded. Main is fully merged; spec_engine and the country specs are unchanged. The shared-spec CI group's remaining local failures (release-target parity regeneration) need the 131 MB local facts feed that CI does not have and are recorded separately.
…aves Every checked borrow of the ASEC source catalogue re-encoded and re-hashed all household and unrepresented records twice, and the survey population preparation's nested seal did it a third time (~2 s per pass on the real catalogue; the native pilot spent ~20% of its CPU there). The catalogue now binds a leaf snapshot to the identity in the same streaming pass at issuance, and later borrows reuse that identity only when the issued root tuples are the same objects, the memo identity equals the owner's retained one, and every projected leaf (literals, keys, person rosters, members' fields) is the very object seen while streaming. Frozen dataclasses stay writable through object.__setattr__, so the leaves carry the proof; any subclass leaf, replaced container, or foreign type keeps the full canonical encoding and its refusals. Inventory scope review for asec_population_catalogue.py: no new imports or resource accesses; the only new unbound uses are the survey_population_domains references inside _eligible_key and _eligible_leaves (exact record/key/source type checks), so the module's unbound_uses digest is re-pinned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erified-lanes-20260910
Main's diagnostics schema 8 (#855) requires a complete hierarchy for every registry-backed target, so the `national.demographic_calibration` node failed on the merge ref: its S0101 registry carried none. Each activated cell now declares the only hierarchy it may have — provider `census_acs`, category `census_acs.population_by_age` (S0101) or `census_acs.population_by_sex_and_age` (B01001), both labels added to the reviewed US variable-label catalog, the activation's own national publisher row (0100000US, "United States", level country), one `publisher_cell` dimension whose value is the cell and whose label is the published one the band binds, and the cell itself as the target so its id equals the spec name. Both S0101 spec builders attach it; the registry declaration round- trips it; the registry validator refuses a spec without exactly the hierarchy rebuilt from its table, name, label and geography; the survey diagnostics verifier's defensive pin moves from schema 6 to 8. Controls: the invented seven-node graph's diagnostics are schema 8 with a hierarchy on every row; the registry JSON round-trips every hierarchy; missing, foreign-cell, foreign-geography and foreign-category hierarchies are refused; the label catalog pin lists the two new rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aled module `survey_origin_budget` seals its own functions in its producer check, so a monkeypatch of `verify_survey_weight_only_successor` on that module made `freeze_survey_origin_budget` refuse with PRODUCER_CHANGED before the seven-node graph ever ran, and the three terminal-manifest mutation controls could never reach the MANIFEST_NODE_STATE refusal they exist to prove. The seam is now a delegating stand-in for the stage's `budgets` alias, so the sealed module is untouched and the mutation lands exactly where it did before: after successor verification, before the final manifest-state check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aves An independent read of c2c6b37 found two in-process changes that alter the canonical encoding without touching a projected leaf: reassigning the `Source.ASEC` member's `_value_` (the encoder writes `source.value`, the memo pinned only member identity), and reassigning `__class__` on a record, key or person to a layout-compatible subclass (the encoder refuses anything but the exact types on every full pass). Both now miss: each key projects `type(key)`, the member and its value object; each household, person and ledger row projects its class and the persons container's class. Two regressions exercise both vectors and the full path's refusal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erified-lanes-20260910 # Conflicts: # packages/microcosm-build/tests/test_spec_engine_country_bundles.py
…mark the stamp as in-process Two attested identities moved with the merge ref, both re-pinned to the values the merged tree computes: the UK country bundle (main's #903 added the frs_relationships spine stage on top of this branch's attested kernel changes, so neither side's pin survives the merge), and the multispine pool tool's constants-adapter country_spec digest, which had already moved to the branch's attested value recorded in docs/evidence/spec-engine/ us-f0-coverage.json when the seed identities were re-pinned but was still asserted at main's value in test_us_multispine_pool_tool.py. survey_atomic_geography._population_stamp now says it is an in-process seal that must never be persisted or compared across reconstructions (microcosm#907 / #909), naming what a cross-call pin has to carry instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
The US builder needs a reproducible path from original ACS and ASEC sources to one complete population, imputed inputs, calibration and verified analysis files. This draft consolidates that implementation with shared graph, atomic-geography, artifact/replay and calibration changes. It is an integration branch; its full completion is not a prerequisite for independent UK work.
The intended order, corrected on 10 September, is ACS + ASEC → complete initial multispine including support/PUF clones → assign one Census block per resulting household → derive larger geographies → remaining imputation and derivation → national/CD calibration → full and pruned exports. Distinct initial clones can receive different constrained draws, keyed by stable source identity plus a clone discriminator. Their locations then remain fixed. The corrected nine-node prefix now clones before block assignment, with nineteen-node financial and twelve-node age extensions. Its 70 inventoried controls plus a separate default-path compatibility check pass on invented inputs; the small native post-clone pilot did not complete final verification within its reviewed resource budget and remains unaccepted. Earlier native acceptance remains explicitly scoped to the prior order.
The PUF operator acts on the combined survey population. Social Security values and unknownness remain source-owned; the two PUF55 routes use nine predictors when the selected total is known and eight when it is unknown. Prior wages are excluded from the launch input profile. National and local exports must preserve the same constructed record values and relationships; calibration weights, scope and sparsity may vary.
Parallel review and dependencies
Coordination corrected after the 10 September team meeting: Maria intentionally stacks her UK graph-node PR on #893; pin its parent revision and coordinate shared-interface changes. Her #900 source/target reconciliation merged on 10 September at 14:54 UTC, clearing that dependency for Anthony's #855 hierarchy/diagnostics and dashboard work. Anthony's staging #896 proceeds independently. Anthony considers #885 superseded; it is reference material, not another runtime implementation to retain or merge. This does not claim the PR is closed or that he has approved #893.
The US integration owner will support Maria's graph with the shared atomic-area adapter, not build a competing UK graph. Keep UK graph-node registration and stage reordering as separate PRs. Record shared dependencies as specific changes and pinned revisions, extracting smaller prerequisites where useful while preserving Maria's intentional stack. Full native US PUF completion does not gate UK source/target repairs, staging or candidate validation. Full graph coverage in both countries remains a requirement for the broader advertised launch.
Verification and remaining work
This branch incorporates main through
6f7571e1ab8c516154289bd8ebf75cb2646f03b5, including Maria's merged #900 target/lookup reconciliation, UK CGT observation-period work #889 and PR CI scheduling #899. The integration preserves both parent histories and introduces exactly the upstream changed paths; dependency metadata parses and the updated worker lock digest matches. No dependency installation or new runtime acceptance is implied. The shared solver retains UK ordinary/informed behavior and the US grouped-bound path; unsupported combinations fail explicitly.The CI diagnostic preserves missing event merge metadata as empty while requiring checkout/event equality and head/base identities. Forty-one environment-only controls were added; the bootstrap test step passed on a preceding commit, while spec-identity derivation still encounters restricted dependency probes. No source-access or bootstrap permissions were broadened.
Remaining release requirements include native execution/replay of the corrected geography order, current-head CI/spec identities, complete native PUF and other model-input coverage, a small full candidate, model/holdout quality, national/CD calibration, progressive scaling, full/pruned readback and a dashboard tied to the exact release file. No dataset release, default consumer promotion or deployment is claimed.
See the review and evidence index, shared US/UK release-path reference, historical native replay record, and companion site/methods draft #72. Typed-model #873 still needs reconciliation; #885 is retained as historical reference following Anthony's supersession decision.
11 September: continuation and CI repair (123650f)
Continued from the 10 September owner's last local state. Landed in order: the two verified but unpushed commits (survey replay-dependency binding; input-coverage profile/diagnostic, calibration-diagnostic option reconstruction, catalogue immutable memo, with their 25/27/20 controls); the uncommitted CI fixes (ASEC v3→v4 exact-source restoration helper carrying the Frame's mass log and metadata, plus its parametrized checkpoint regression; the two ASEC source test modules the coverage-authentication test imports; the strict-leaf audit stub's
include_consumerskeyword; the 21-node PUF55 recipient count after post-clone geography; the wheels gate installingmicrocosm-frame[us]after the base boundary check); and the incomplete native pilot observation.Then the failures CI had not yet surfaced (collection errors had masked them):
predict_from_uniforms). Main is fully merged and spec_engine is unchanged./proc/statlayout read and urllib3's loopback IPv6 probe from_has_ipv6only, and its kernel-module roster includesmicrocosm.calibrate.gatesand.initialization, which the seed protocol has always listed.Local verification on this head:
test_us_spine_blindness.pycomplete file green; the seventest_spec_seed_*files green (122); the touched ASEC/ACS/PUF55 suites green;tools/spec_engine_coverage.py --checkclean; ruff check/format clean. The diagnostic refuses non-Linux hosts, so its end-to-end proof is the CI job itself.11 September, later: native pilot result, ASEC records memo, main merged (9fba910)
Native pilot v2 (guarded, local, 1/1000 sample, same seeds/estimator count/limits as 10 September, snapshot 123650f with the ACS catalogue memo): all nineteen nodes completed inside the caps for the first time (financial runner 4,744 s CPU; 5,615 s CPU / 5,665 s wall of the 7,200 / 10,800 limits; peak RSS 11.6 GB; no unexpected boundary refusals). The run then failed in the harness's own final seal (
geography_pins == stored_geography_artifacts, status INCOMPLETE). Reproduced on the invented fixture: the harness pinned each geography stage withsurvey_atomic_geography._population_stamp, which is an in-process mutation seal —microcosm.graph.population._storage_partshashes object-dtype storage (the strata axis) as rawndarray.tobytes(), i.e. PyObject pointers, so two independent reconstructions of equal content (frame identity, mass ledger and owners all equal) never agree. Every in-repo use of that stamp compares the same objects, which is why the graph itself ran end to end; the harness helper now pins populations by content identity (_frame_identityplus version/owners/weight kind/mass ledger/design weights), verified equal back-to-back and acrosscheck_atomic_survey_financial_runon the fixture. Worth remembering repo-side:_population_stampmust never be persisted or compared across reconstructions.ASEC records memo (c2c6b37): the pilot's retained phase transitions attributed about a fifth of its CPU to
asec_population_catalogue._records_identity, which every checked borrow of the catalogue re-ran twice and the preparation's nested seal a third time (about 2 s per pass on the real catalogue). The catalogue now binds a leaf snapshot to the identity in the same streaming pass at issuance; later borrows reuse the identity only when the issued root tuples are the same objects, the memo identity equals the owner's retained one, and every projected leaf (literals, keys, person rosters and members' fields) is the very object seen while streaming. Frozen dataclasses stay writable throughobject.__setattr__, so the leaves carry the proof; subclass leaves, replaced containers or foreign types keep the full canonical encoding and its refusals. Seventeen controls intest_us_asec_catalogue_records_memo.py, including real issuance: zero identity recomputations per borrow,RECORDS_CHANGEDandNESTED_EVIDENCE_CHANGEDon an in-place leaf change. The module's inventoryunbound_usesdigest is re-pinned (only new uses:survey_population_domainstype checks in_eligible_key/_eligible_leaves; no new imports or resource accesses). The larger cost — each checked borrow re-verifying the whole ancestor chain, includingasec_current_money_source._frame_signature— is structural and left as is.Main merged (9fba910; #855 schema-7 geography dimensions, #902 state tax metadata). The only conflict was
microcosm-calibrate/__init__.py, resolved by keeping both import blocks.tools/spec_engine_coverage.py --checkandtools/ci_test_groups.py --verifyare clean on the merged tree.Native pilot v3 (snapshot 9fba910, same sample/seeds/limits, helper pin fix) is running locally under the guard; its receipt will be recorded here when it completes, accepted or not.
11 September, evening: schema-8 hierarchies, a sealed-module test seam, memo hardening (2ca11c8)
national.demographic_calibrationfailed on the merge ref with none. Each activated S0101/B01001 cell now carries the only hierarchy it may have (providercensus_acs; categorycensus_acs.population_by_age/population_by_sex_and_age, both labels added to the reviewed US variable-label catalog; the activation's own national publisher row; onepublisher_celldimension; the cell as target). Both spec builders attach it, the registry declaration round-trips it, the validator refuses anything but the hierarchy rebuilt from table, name, label and geography, and the survey diagnostics verifier's defensive pin moves 6→8.survey_origin_budgetseals its own functions, so the tests' monkeypatch ofverify_survey_weight_only_successorrefused withPRODUCER_CHANGEDbefore the seven-node graph ran. The seam is now injected through the stage'sbudgetsalias; the sealed module is untouched and the mutation lands where it did before.Source.ASECmember's value object and every record/key/person class are now projected leaves, so_value_tampering and__class__reassignment miss the memo and reach the full path's refusals; two regressions.spec_engine_coverage --check,ci_test_groups --verify,ruff checkclean.11 September, 14:52 ET: native pilot v4 accepted (snapshot 2ca11c8)
Guarded local rerun of the 1/1000 native post-clone nineteen-node pilot on this head (same sample, seeds 20260908,
n_estimators=2, demographic conditioning, limits 7,200 s CPU / 10,800 s wall / 32 GB): passed — guard result 0, 1 collected, outcomes["passed"]; 5,279 s CPU, 5,362 s wall, peak RSS 9.3 GB; source, resource, owned and native pins unchanged; 23 known boundary refusals, none unexpected;native-financial-manifest.jsonstatusACTUAL_ARTIFACTS_RECORDED,release_eligible: false,guard_completion_required: true,cross_process_source_authority_granted: false. Entity rows: 1,584 households / 3,464 people at create and raw, 3,168 / 6,928 after expansion and through geography and financial. Nine prefix nodes (175 s wall) and nineteen extension nodes (968 s wall; the three predictor sources ≈ 260–300 s each); financial runner 4,686 s wall including the requalifying prefix borrows; export read-back 211 s; final seals passed. Compared with v2 (snapshot 123650f): 336 s less CPU with the ASEC records memo, and the harness's cross-reconstruction geography pin now holds. A v3 attempt on 9fba910 died at 65 min with no receipt or refusal (no memory pressure, no app restart; treated as an external process kill), so this run is the first accepted receipt. Evidence:~/PolicyEngine/_recovered/scratch-backup/893/pilot-v4-20260911/(receipt, manifest, resource progress, exports). Development scope only; no release certification claimed.11 September, 15:10 ET: main #903 merged, two identities re-pinned (77c9e78)
Main's #903 (UK household composition, frs_relationships spine stage) is merged; the only conflict was the UK country-bundle digest in
test_spec_engine_country_bundles.py, re-pinned to the value the merged tree computes (neither side's pin survives a merge that changes attested modules on both sides). Theengine-us us-amfailure on 2ca11c8 was the multispine pool tool's constants-adaptercountry_specdigest, still asserted at main's value whiledocs/evidence/spec-engine/us-f0-coverage.jsonalready carried this branch's attested value from the seed re-pin; the test now asserts that value._population_stampinsurvey_atomic_geography.pydocuments that it is an in-process seal (see #907 / #909 for the repo-side fix on main).