diff --git a/docs/design/track_b_b0_2_counts.json b/docs/design/track_b_b0_2_counts.json new file mode 100644 index 00000000..50bd1ac2 --- /dev/null +++ b/docs/design/track_b_b0_2_counts.json @@ -0,0 +1,2201 @@ +{ + "b0_1_counts_sha256": "e93826d4c535a41ea28155b837307a1a941f9ac9d205f8e8bf60421481e54f99", + "counts": { + "b3": { + "by_wave": { + "1999": { + "62_64": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 60, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 200, + "kish_n_eff": 170, + "persons": 210, + "valid_rows": 210 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 50, + "design_strata": 30, + "household_cluster_worst_n_eff": 110, + "households": 150, + "kish_n_eff": 120, + "persons": 160, + "valid_rows": 160 + }, + "67_69": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 160, + "households": 210, + "kish_n_eff": 200, + "persons": 240, + "valid_rows": 240 + } + }, + "2001": { + "62_64": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 50, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 230, + "kish_n_eff": 190, + "persons": 250, + "valid_rows": 250 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 60, + "design_strata": 40, + "household_cluster_worst_n_eff": 100, + "households": 130, + "kish_n_eff": 110, + "persons": 140, + "valid_rows": 140 + }, + "67_69": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 60, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 220, + "kish_n_eff": 190, + "persons": 240, + "valid_rows": 240 + } + }, + "2003": { + "62_64": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 170, + "households": 240, + "kish_n_eff": 200, + "persons": 260, + "valid_rows": 260 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 60, + "design_strata": 40, + "household_cluster_worst_n_eff": 110, + "households": 150, + "kish_n_eff": 120, + "persons": 160, + "valid_rows": 160 + }, + "67_69": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 60, + "design_strata": 40, + "household_cluster_worst_n_eff": 140, + "households": 190, + "kish_n_eff": 160, + "persons": 210, + "valid_rows": 210 + } + }, + "2005": { + "62_64": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 50, + "household_cluster_worst_n_eff": 170, + "households": 250, + "kish_n_eff": 220, + "persons": 280, + "valid_rows": 280 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 110, + "households": 170, + "kish_n_eff": 130, + "persons": 170, + "valid_rows": 170 + }, + "67_69": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 140, + "households": 200, + "kish_n_eff": 160, + "persons": 210, + "valid_rows": 210 + } + }, + "2007": { + "62_64": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 200, + "households": 310, + "kish_n_eff": 240, + "persons": 330, + "valid_rows": 330 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 50, + "household_cluster_worst_n_eff": 120, + "households": 170, + "kish_n_eff": 140, + "persons": 180, + "valid_rows": 180 + }, + "67_69": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 50, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 220, + "kish_n_eff": 180, + "persons": 240, + "valid_rows": 240 + } + }, + "2009": { + "62_64": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 40, + "household_cluster_worst_n_eff": 270, + "households": 370, + "kish_n_eff": 330, + "persons": 410, + "valid_rows": 410 + }, + "65_66": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 50, + "household_cluster_worst_n_eff": 150, + "households": 200, + "kish_n_eff": 180, + "persons": 220, + "valid_rows": 220 + }, + "67_69": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 50, + "design_clusters": 70, + "design_strata": 50, + "household_cluster_worst_n_eff": 190, + "households": 230, + "kish_n_eff": 200, + "persons": 250, + "valid_rows": 250 + } + }, + "2011": { + "62_64": { + "accuracy_positive_rows": 30, + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 330, + "households": 500, + "kish_n_eff": 410, + "persons": 570, + "valid_rows": 570 + }, + "65_66": { + "accuracy_positive_rows": "<10", + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 200, + "kish_n_eff": 170, + "persons": 210, + "valid_rows": 210 + }, + "67_69": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 180, + "households": 270, + "kish_n_eff": 220, + "persons": 300, + "valid_rows": 300 + } + }, + "2013": { + "62_64": { + "accuracy_positive_rows": 30, + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 360, + "households": 520, + "kish_n_eff": 430, + "persons": 580, + "valid_rows": 580 + }, + "65_66": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 40, + "household_cluster_worst_n_eff": 220, + "households": 320, + "kish_n_eff": 260, + "persons": 350, + "valid_rows": 350 + }, + "67_69": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 210, + "households": 300, + "kish_n_eff": 250, + "persons": 330, + "valid_rows": 330 + } + }, + "2015": { + "62_64": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 60, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 380, + "households": 590, + "kish_n_eff": 450, + "persons": 650, + "valid_rows": 650 + }, + "65_66": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 230, + "households": 340, + "kish_n_eff": 260, + "persons": 360, + "valid_rows": 360 + }, + "67_69": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 260, + "households": 370, + "kish_n_eff": 320, + "persons": 420, + "valid_rows": 420 + } + }, + "2017": { + "62_64": { + "accuracy_positive_rows": 40, + "design_cluster_worst_n_eff": 60, + "design_clusters": 110, + "design_strata": 60, + "household_cluster_worst_n_eff": 420, + "households": 640, + "kish_n_eff": 490, + "persons": 710, + "valid_rows": 710 + }, + "65_66": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 60, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 250, + "households": 370, + "kish_n_eff": 280, + "persons": 400, + "valid_rows": 400 + }, + "67_69": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 310, + "households": 450, + "kish_n_eff": 370, + "persons": 510, + "valid_rows": 510 + } + }, + "2019": { + "62_64": { + "accuracy_positive_rows": 40, + "design_cluster_worst_n_eff": 60, + "design_clusters": 110, + "design_strata": 60, + "household_cluster_worst_n_eff": 350, + "households": 610, + "kish_n_eff": 420, + "persons": 680, + "valid_rows": 680 + }, + "65_66": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 60, + "design_clusters": 90, + "design_strata": 60, + "household_cluster_worst_n_eff": 250, + "households": 420, + "kish_n_eff": 280, + "persons": 450, + "valid_rows": 450 + }, + "67_69": { + "accuracy_positive_rows": 10, + "design_cluster_worst_n_eff": 60, + "design_clusters": 100, + "design_strata": 60, + "household_cluster_worst_n_eff": 320, + "households": 480, + "kish_n_eff": 350, + "persons": 530, + "valid_rows": 530 + } + }, + "2021": { + "62_64": { + "accuracy_positive_rows": 30, + "design_cluster_worst_n_eff": 50, + "design_clusters": 110, + "design_strata": 60, + "household_cluster_worst_n_eff": 320, + "households": 570, + "kish_n_eff": 390, + "persons": 640, + "valid_rows": 640 + }, + "65_66": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 60, + "design_clusters": 100, + "design_strata": 60, + "household_cluster_worst_n_eff": 250, + "households": 400, + "kish_n_eff": 280, + "persons": 420, + "valid_rows": 420 + }, + "67_69": { + "accuracy_positive_rows": 20, + "design_cluster_worst_n_eff": 70, + "design_clusters": 100, + "design_strata": 60, + "household_cluster_worst_n_eff": 340, + "households": 560, + "kish_n_eff": 410, + "persons": 630, + "valid_rows": 630 + } + }, + "2023": { + "62_64": { + "accuracy_positive_rows": 60, + "design_cluster_worst_n_eff": 60, + "design_clusters": 110, + "design_strata": 60, + "household_cluster_worst_n_eff": 340, + "households": 570, + "kish_n_eff": 390, + "persons": 630, + "valid_rows": 630 + }, + "65_66": { + "accuracy_positive_rows": 30, + "design_cluster_worst_n_eff": 60, + "design_clusters": 90, + "design_strata": 60, + "household_cluster_worst_n_eff": 210, + "households": 360, + "kish_n_eff": 240, + "persons": 390, + "valid_rows": 390 + }, + "67_69": { + "accuracy_positive_rows": 40, + "design_cluster_worst_n_eff": 60, + "design_clusters": 100, + "design_strata": 60, + "household_cluster_worst_n_eff": 320, + "households": 530, + "kish_n_eff": 380, + "persons": 600, + "valid_rows": 600 + } + } + }, + "distinct_persons_by_band": { + "62_64": 4430, + "65_66": 3550, + "67_69": 3380 + }, + "lag_pairs": { + "2": { + "1999_2001": { + "62_64": 240, + "65_66": 130, + "67_69": 230 + }, + "2001_2003": { + "62_64": 250, + "65_66": 150, + "67_69": 200 + }, + "2003_2005": { + "62_64": 270, + "65_66": 170, + "67_69": 210 + }, + "2005_2007": { + "62_64": 330, + "65_66": 180, + "67_69": 230 + }, + "2007_2009": { + "62_64": 400, + "65_66": 210, + "67_69": 250 + }, + "2009_2011": { + "62_64": 560, + "65_66": 210, + "67_69": 300 + }, + "2011_2013": { + "62_64": 570, + "65_66": 340, + "67_69": 320 + }, + "2013_2015": { + "62_64": 630, + "65_66": 350, + "67_69": 420 + }, + "2015_2017": { + "62_64": 660, + "65_66": 390, + "67_69": 490 + }, + "2017_2019": { + "62_64": 660, + "65_66": 440, + "67_69": 510 + }, + "2019_2021": { + "62_64": 630, + "65_66": 420, + "67_69": 610 + }, + "2021_2023": { + "62_64": 620, + "65_66": 380, + "67_69": 590 + } + }, + "4": { + "1999_2003": { + "62_64": 250, + "65_66": 150, + "67_69": 200 + }, + "2001_2005": { + "62_64": 270, + "65_66": 160, + "67_69": 200 + }, + "2003_2007": { + "62_64": 320, + "65_66": 180, + "67_69": 230 + }, + "2005_2009": { + "62_64": 400, + "65_66": 210, + "67_69": 240 + }, + "2007_2011": { + "62_64": 550, + "65_66": 210, + "67_69": 290 + }, + "2009_2013": { + "62_64": 570, + "65_66": 330, + "67_69": 320 + }, + "2011_2015": { + "62_64": 620, + "65_66": 350, + "67_69": 410 + }, + "2013_2017": { + "62_64": 640, + "65_66": 380, + "67_69": 490 + }, + "2015_2019": { + "62_64": 620, + "65_66": 420, + "67_69": 500 + }, + "2017_2021": { + "62_64": 620, + "65_66": 410, + "67_69": 610 + }, + "2019_2023": { + "62_64": 610, + "65_66": 380, + "67_69": 580 + } + } + } + }, + "b3l": { + "later_support": { + "2009": { + "25_44": { + "kish_n_eff": 3210, + "persons": 5210 + }, + "45_61": { + "kish_n_eff": 2950, + "persons": 4350 + }, + "62_64": { + "kish_n_eff": 300, + "persons": 400 + }, + "65_66": { + "kish_n_eff": 160, + "persons": 210 + }, + "67_69": { + "kish_n_eff": 190, + "persons": 250 + } + }, + "2011": { + "25_44": { + "kish_n_eff": 2860, + "persons": 4710 + }, + "45_61": { + "kish_n_eff": 2790, + "persons": 4140 + }, + "62_64": { + "kish_n_eff": 410, + "persons": 550 + }, + "65_66": { + "kish_n_eff": 160, + "persons": 210 + }, + "67_69": { + "kish_n_eff": 220, + "persons": 290 + } + }, + "2013": { + "25_44": { + "kish_n_eff": 2490, + "persons": 4160 + }, + "45_61": { + "kish_n_eff": 2630, + "persons": 3930 + }, + "62_64": { + "kish_n_eff": 410, + "persons": 560 + }, + "65_66": { + "kish_n_eff": 250, + "persons": 330 + }, + "67_69": { + "kish_n_eff": 240, + "persons": 320 + } + }, + "2015": { + "25_44": { + "kish_n_eff": 2090, + "persons": 3470 + }, + "45_61": { + "kish_n_eff": 2410, + "persons": 3630 + }, + "62_64": { + "kish_n_eff": 430, + "persons": 610 + }, + "65_66": { + "kish_n_eff": 250, + "persons": 340 + }, + "67_69": { + "kish_n_eff": 310, + "persons": 410 + } + }, + "2017": { + "25_44": { + "kish_n_eff": 1800, + "persons": 3000 + }, + "45_61": { + "kish_n_eff": 2230, + "persons": 3390 + }, + "62_64": { + "kish_n_eff": 430, + "persons": 620 + }, + "65_66": { + "kish_n_eff": 270, + "persons": 370 + }, + "67_69": { + "kish_n_eff": 350, + "persons": 480 + } + }, + "2019": { + "25_44": { + "kish_n_eff": 1450, + "persons": 2450 + }, + "45_61": { + "kish_n_eff": 2070, + "persons": 3180 + }, + "62_64": { + "kish_n_eff": 410, + "persons": 600 + }, + "65_66": { + "kish_n_eff": 280, + "persons": 400 + }, + "67_69": { + "kish_n_eff": 330, + "persons": 470 + } + }, + "2021": { + "25_44": { + "kish_n_eff": 1090, + "persons": 1860 + }, + "45_61": { + "kish_n_eff": 1880, + "persons": 2910 + }, + "62_64": { + "kish_n_eff": 360, + "persons": 550 + }, + "65_66": { + "kish_n_eff": 260, + "persons": 370 + }, + "67_69": { + "kish_n_eff": 400, + "persons": 570 + } + }, + "2023": { + "25_44": { + "kish_n_eff": 820, + "persons": 1410 + }, + "45_61": { + "kish_n_eff": 1820, + "persons": 2840 + }, + "62_64": { + "kish_n_eff": 360, + "persons": 550 + }, + "65_66": { + "kish_n_eff": 230, + "persons": 340 + }, + "67_69": { + "kish_n_eff": 380, + "persons": 520 + } + } + }, + "origin_wave": 2007, + "rosters": { + "D2006": { + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 470, + "households": 840, + "kish_n_eff": 560, + "persons": 980 + }, + "25_44": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 2300, + "households": 3980, + "kish_n_eff": 3550, + "persons": 5760 + }, + "45_61": { + "design_cluster_worst_n_eff": 60, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 1920, + "households": 3030, + "kish_n_eff": 2970, + "persons": 4370 + }, + "62_64": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 200, + "households": 310, + "kish_n_eff": 240, + "persons": 330 + }, + "65_66": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 50, + "household_cluster_worst_n_eff": 120, + "households": 170, + "kish_n_eff": 140, + "persons": 180 + }, + "67_69": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 220, + "kish_n_eff": 180, + "persons": 240 + }, + "70_79": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 350, + "households": 500, + "kish_n_eff": 490, + "persons": 620 + }, + "80_plus": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 240, + "households": 320, + "kish_n_eff": 310, + "persons": 370 + } + }, + "overall": { + "design_cluster_worst_n_eff": 60, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 5050, + "households": 8290, + "kish_n_eff": 8210, + "persons": 12840 + }, + "unknown_age": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + } + }, + "R2006_FULL": { + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 2170, + "households": 4590, + "kish_n_eff": 5320, + "persons": 9310 + }, + "25_44": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 2500, + "households": 4360, + "kish_n_eff": 3870, + "persons": 6330 + }, + "45_61": { + "design_cluster_worst_n_eff": 60, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 1990, + "households": 3150, + "kish_n_eff": 3070, + "persons": 4560 + }, + "62_64": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 210, + "households": 320, + "kish_n_eff": 250, + "persons": 350 + }, + "65_66": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 130, + "households": 190, + "kish_n_eff": 150, + "persons": 200 + }, + "67_69": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 150, + "households": 230, + "kish_n_eff": 190, + "persons": 250 + }, + "70_79": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 370, + "households": 560, + "kish_n_eff": 520, + "persons": 670 + }, + "80_plus": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 260, + "households": 370, + "kish_n_eff": 330, + "persons": 420 + } + }, + "overall": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 4570, + "households": 8290, + "kish_n_eff": 13360, + "persons": 22100 + }, + "unknown_age": { + "design_cluster_worst_n_eff": "<10", + "design_clusters": "<10", + "design_strata": "<10", + "household_cluster_worst_n_eff": "<10", + "households": "<10", + "kish_n_eff": "<10", + "persons": "<10" + } + } + } + }, + "outside_b2": { + "b2_households_by_wave": { + "2013": 8270, + "2015": 7730 + }, + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "25_44": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "45_61": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "62_64": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "65_66": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "67_69": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "70_79": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "80_plus": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + } + }, + "overall": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "unknown_age": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + } + }, + "rosters": { + "R_B2": { + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 60, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 430, + "households": 830, + "kish_n_eff": 500, + "persons": 950 + }, + "25_44": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 2380, + "households": 4230, + "kish_n_eff": 3600, + "persons": 6090 + }, + "45_61": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 1920, + "households": 3110, + "kish_n_eff": 2870, + "persons": 4400 + }, + "62_64": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 330, + "households": 500, + "kish_n_eff": 410, + "persons": 570 + }, + "65_66": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 150, + "households": 200, + "kish_n_eff": 170, + "persons": 210 + }, + "67_69": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 180, + "households": 270, + "kish_n_eff": 220, + "persons": 300 + }, + "70_79": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 330, + "households": 500, + "kish_n_eff": 460, + "persons": 590 + }, + "80_plus": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 70, + "design_strata": 40, + "household_cluster_worst_n_eff": 270, + "households": 360, + "kish_n_eff": 350, + "persons": 420 + } + }, + "overall": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 5250, + "households": 8910, + "kish_n_eff": 8350, + "persons": 13540 + }, + "unknown_age": { + "design_cluster_worst_n_eff": "<10", + "design_clusters": "<10", + "design_strata": "<10", + "household_cluster_worst_n_eff": "<10", + "households": "<10", + "kish_n_eff": "<10", + "persons": "<10" + } + }, + "R_FULL": { + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 2110, + "households": 4700, + "kish_n_eff": 5150, + "persons": 9510 + }, + "25_44": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 2590, + "households": 4640, + "kish_n_eff": 3920, + "persons": 6730 + }, + "45_61": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 1990, + "households": 3250, + "kish_n_eff": 2980, + "persons": 4620 + }, + "62_64": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 350, + "households": 540, + "kish_n_eff": 430, + "persons": 610 + }, + "65_66": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 160, + "households": 220, + "kish_n_eff": 180, + "persons": 240 + }, + "67_69": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 190, + "households": 310, + "kish_n_eff": 230, + "persons": 340 + }, + "70_79": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 350, + "households": 530, + "kish_n_eff": 480, + "persons": 630 + }, + "80_plus": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 290, + "households": 400, + "kish_n_eff": 370, + "persons": 460 + } + }, + "overall": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 4680, + "households": 8910, + "kish_n_eff": 13450, + "persons": 23130 + }, + "unknown_age": { + "design_cluster_worst_n_eff": "<10", + "design_clusters": "<10", + "design_strata": "<10", + "household_cluster_worst_n_eff": "<10", + "households": "<10", + "kish_n_eff": "<10", + "persons": "<10" + } + }, + "R_TA_PROXY": { + "by_age_band": { + "1_24": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + }, + "25_44": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 120, + "design_strata": 60, + "household_cluster_worst_n_eff": 1860, + "households": 3340, + "kish_n_eff": 2800, + "persons": 4680 + }, + "45_61": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 1990, + "households": 3250, + "kish_n_eff": 2980, + "persons": 4620 + }, + "62_64": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 350, + "households": 540, + "kish_n_eff": 430, + "persons": 610 + }, + "65_66": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 160, + "households": 220, + "kish_n_eff": 180, + "persons": 240 + }, + "67_69": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 190, + "households": 310, + "kish_n_eff": 230, + "persons": 340 + }, + "70_79": { + "design_cluster_worst_n_eff": 50, + "design_clusters": 90, + "design_strata": 50, + "household_cluster_worst_n_eff": 350, + "households": 530, + "kish_n_eff": 480, + "persons": 630 + }, + "80_plus": { + "design_cluster_worst_n_eff": 40, + "design_clusters": 80, + "design_strata": 50, + "household_cluster_worst_n_eff": 290, + "households": 400, + "kish_n_eff": 370, + "persons": 460 + } + }, + "overall": { + "design_cluster_worst_n_eff": 70, + "design_clusters": 130, + "design_strata": 60, + "household_cluster_worst_n_eff": 4330, + "households": 7240, + "kish_n_eff": 7350, + "persons": 11570 + }, + "unknown_age": { + "design_cluster_worst_n_eff": 0, + "design_clusters": 0, + "design_strata": 0, + "household_cluster_worst_n_eff": 0, + "households": 0, + "kish_n_eff": 0, + "persons": 0 + } + } + } + }, + "disclosure": { + "base": 10, + "coarsening_rule": "0 stays 0; 1 to B-1 is = 4, shown by two exactly verified integer points, under the published values, the guaranteed cross-type relations and every target the release discloses to lie outside 1-9, or is exempt: its proven least and greatest sizes under those are the ones B0.1's published counts alone give it; every program is set from the publications alone, and a target not shown wide or exempt within 300 seconds per program is narrow", + "status": "released" + }, + "geometry": { + "age_convention": "collection_wave", + "collection_waves": [ + 1999, + 2001, + 2003, + 2005, + 2007, + 2009, + 2011, + 2013, + 2015, + 2017, + 2019, + 2021, + 2023 + ], + "lag_years": [ + 2, + 4 + ], + "reference_years": [ + 1998, + 2000, + 2002, + 2004, + 2006, + 2008, + 2010, + 2012, + 2014, + 2016, + 2018, + 2020, + 2022 + ] + }, + "psid_files_opened_sha256": { + "family/1999/FAM1999ER.sps": "7afad10e0da6969cd434a588f7d336923451d7a2fa8a71959c6edf376b023a9b", + "family/1999/FAM1999ER.txt": "92d7bef0eb5794cccb9137a4fe177dadfb2ba92709628b5cbc4004f8e7d786a0", + "family/2001/FAM2001ER.sps": "c6dc0e81131ece42ebeb70a67ee58082b939049815bd359724e745780d0afc12", + "family/2001/FAM2001ER.txt": "cc55d16f2fd44bcc89c1ab812e21f3f4f7bc26b15645b3170e590f55d1320ca5", + "family/2003/FAM2003ER.sps": "09f96449a9410a2478c0c24c042b5510ea0a75e7c97d83102a60d1faab747ac3", + "family/2003/FAM2003ER.txt": "f06914b61c017d34c471343d9667d2c19ad5d59f78890ee26ff613d465555ef1", + "family/2005/FAM2005ER.sps": "9a3440f8c3ba8af3ab2a40bde19b3a7382e20b82b0d91bd7b4ccf5e36ee7ec0d", + "family/2005/FAM2005ER.txt": "29e9d9a3eb7cdf33df8831cc55f27ab6e230e8202b4cdd982da79b759c0a749f", + "family/2007/FAM2007ER.sps": "4c8bd5b1d7bdce8a9d596e191f2791004ceb3413bf0b88c68cadccd33277977d", + "family/2007/FAM2007ER.txt": "f811e277303fe90a42edef602ad1a92405e8df0c9ee0dd36eff4ff531202ebbf", + "family/2009/FAM2009ER.sps": "4faa45e30b5a4719a3fa12f9d5d48568f644f315457ee3eed3e4ff8872459e4c", + "family/2009/FAM2009ER.txt": "59cf31106cf4c3d2e62903bd841b1689c9229e15674bdc5fd896453286b085f2", + "family/2011/FAM2011ER.sps": "6a994d8b65803a515eaa3bf62c74e278dd5943b8b578428ef4fd462d96cba45c", + "family/2011/FAM2011ER.txt": "3253586567a76a43d3e4a568dd5ca8af0f8a3c69db829d6a55bdf0942c60992f", + "family/2013/FAM2013ER.sps": "526595b50a2087adc635da3c1288a396aa7179b075c11506f10338ab6348a0b5", + "family/2013/FAM2013ER.txt": "0406aed042d660f51712d0f18a6e80f939717c0c2fc68b8d3a8d6dcbf48180e1", + "family/2015/FAM2015ER.sps": "6500c8e88a2353e7a6c5322a898f3879662228fdefa329e0901eb254fc9e3fff", + "family/2015/FAM2015ER.txt": "e4a64b275a9c6dbe8e7b562be7ed0906d4082b0c49cad79d764c69b8de318ec2", + "family/2017/FAM2017ER.sps": "24abae319022d4e3da9bce8052455dfee6b00b7b9c642004eddcfa06cb8d59e5", + "family/2017/FAM2017ER.txt": "c4220fe0569f3dbb8e110029a9d4d896c41635c50c86b7cb9edd0e7a5de82b0f", + "family/2019/FAM2019ER.sps": "c9f8ba8698779aeaf239c0eea4b1f98d32975b1c77767acfd35601fcb883a5b0", + "family/2019/FAM2019ER.txt": "80602015789c552cff3b75dfec6813204e918837f767f2e71744f51c07a9056d", + "family/2021/FAM2021ER.sps": "50526906dafd86271074ba15fd426ab7030c2b62f29acf3d819b577fdc9442e2", + "family/2021/FAM2021ER.txt": "c8a7a0589962bd3e6300e99595be16cb198ef48b3bb2b9b626743fe44cd185ae", + "family/2023/FAM2023ER.sps": "915755685965a41743e96e42abbbfdddd7eab1df33234e462b0813b25d4df169", + "family/2023/FAM2023ER.txt": "384c63924b6667ecebd7b3ca797bfae94fcea4c0de1708c46ab25659a16432b2", + "ind2023er/IND2023ER.sps": "92d32d0efd65d50e984e971c681f6e9231b720973444cdc8f20f397039c9a462", + "ind2023er/IND2023ER.txt": "d68554ac828a690491572f37323eba1d8488e8b0001ed2aa89df4d4ade26c869" + }, + "repository_head": "e8a9fac09b600ddfdaa586f962a49a351d9386b7", + "requested_label_allowlist": { + "anchor_2011": { + "age": { + "label": "AGE OF INDIVIDUAL 11", + "variable": "ER34104" + }, + "interview": { + "label": "2011 INTERVIEW NUMBER", + "variable": "ER34101" + }, + "interview_1968": { + "label": "1968 INTERVIEW NUMBER", + "variable": "ER30001" + }, + "person_number": { + "label": "PERSON NUMBER 68", + "variable": "ER30002" + }, + "relationship": { + "label": "RELATION TO HEAD 11", + "variable": "ER34103" + }, + "reported_birth_year": { + "label": "YEAR INDIVIDUAL BORN 11", + "variable": "ER34106" + }, + "sequence": { + "label": "SEQUENCE NUMBER 11", + "variable": "ER34102" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 11", + "variable": "ER34155" + } + }, + "design": { + "cluster": { + "label": "SAMPLING ERROR CLUSTER", + "variable": "ER31997" + }, + "interview_1968": { + "label": "1968 INTERVIEW NUMBER", + "variable": "ER30001" + }, + "person_number": { + "label": "PERSON NUMBER 68", + "variable": "ER30002" + }, + "stratum": { + "label": "SAMPLING ERROR STRATUM", + "variable": "ER31996" + } + }, + "family": { + "1999": { + "head_accuracy": [ + { + "label": "ACC LABOR INCOME-HD", + "variable": "ER16464" + } + ], + "head_labor": { + "label": "LABOR INCOME-HEAD", + "variable": "ER16463" + }, + "interview": { + "label": "1999 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER13002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME-WF", + "variable": "ER16466" + } + ], + "spouse_labor": { + "label": "LABOR INCOME-WIFE", + "variable": "ER16465" + } + }, + "2001": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2000", + "variable": "ER20426" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2000", + "variable": "ER20440" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2000", + "variable": "ER20443" + }, + "interview": { + "label": "2001 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER17002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2000", + "variable": "ER20448" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2000", + "variable": "ER20447" + } + }, + "2003": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD LAST YEAR", + "variable": "ER24118" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD LAST YEAR", + "variable": "ER24134" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD LAST YEAR", + "variable": "ER24116" + }, + "interview": { + "label": "2003 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER21002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE LAST YEAR", + "variable": "ER24136" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE LAST YEAR", + "variable": "ER24135" + } + }, + "2005": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2004", + "variable": "ER27914" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2004", + "variable": "ER27930" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2004", + "variable": "ER27931" + }, + "interview": { + "label": "2005 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER25002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2004", + "variable": "ER27944" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2004", + "variable": "ER27943" + } + }, + "2007": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2006", + "variable": "ER40904" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2006", + "variable": "ER40920" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2006", + "variable": "ER40921" + }, + "interview": { + "label": "2007 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER36002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2006", + "variable": "ER40934" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2006", + "variable": "ER40933" + } + }, + "2009": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2008", + "variable": "ER46812" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2008", + "variable": "ER46828" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2008", + "variable": "ER46829" + }, + "interview": { + "label": "2009 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER42002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2008", + "variable": "ER46842" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2008", + "variable": "ER46841" + } + }, + "2011": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2010", + "variable": "ER52220" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2010", + "variable": "ER52236" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2010", + "variable": "ER52237" + }, + "interview": { + "label": "2011 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER47302" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2010", + "variable": "ER52250" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2010", + "variable": "ER52249" + } + }, + "2013": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2012", + "variable": "ER58021" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2012", + "variable": "ER58037" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2012", + "variable": "ER58038" + }, + "interview": { + "label": "2013 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER53002" + }, + "spouse_accuracy": [ + { + "label": "ACC LABOR INCOME OF WIFE-2012", + "variable": "ER58051" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF WIFE-2012", + "variable": "ER58050" + } + }, + "2015": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF HEAD-2014", + "variable": "ER65201" + }, + { + "label": "ACC MISC LABOR INCOME OF HEAD-2014", + "variable": "ER65215" + } + ], + "head_labor": { + "label": "LABOR INCOME OF HEAD-2014", + "variable": "ER65216" + }, + "interview": { + "label": "2015 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER60002" + }, + "spouse_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF SPOUSE-2014", + "variable": "ER65229" + }, + { + "label": "ACC MISC LABOR INCOME OF SPOUSE-2014", + "variable": "ER65243" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF SPOUSE-2014", + "variable": "ER65244" + } + }, + "2017": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF RP-2016", + "variable": "ER71278" + }, + { + "label": "ACC MISC LABOR INCOME OF RP-2016", + "variable": "ER71292" + } + ], + "head_labor": { + "label": "LABOR INCOME OF REF PERSON-2016", + "variable": "ER71293" + }, + "interview": { + "label": "2017 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER66002" + }, + "spouse_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF SPOUSE-2016", + "variable": "ER71306" + }, + { + "label": "ACC MISC LABOR INCOME OF SPOUSE-2016", + "variable": "ER71320" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF SPOUSE-2016", + "variable": "ER71321" + } + }, + "2019": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF RP-2018", + "variable": "ER77300" + }, + { + "label": "ACC MISC LABOR INCOME OF RP-2018", + "variable": "ER77314" + } + ], + "head_labor": { + "label": "LABOR INCOME OF REF PERSON-2018", + "variable": "ER77315" + }, + "interview": { + "label": "2019 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER72002" + }, + "spouse_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF SPOUSE-2018", + "variable": "ER77328" + }, + { + "label": "ACC MISC LABOR INCOME OF SPOUSE-2018", + "variable": "ER77342" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF SPOUSE-2018", + "variable": "ER77343" + } + }, + "2021": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF RP-2020", + "variable": "ER81627" + }, + { + "label": "ACC MISC LABOR INCOME OF RP-2020", + "variable": "ER81641" + } + ], + "head_labor": { + "label": "LABOR INCOME OF REF PERSON-2020", + "variable": "ER81642" + }, + "interview": { + "label": "2021 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER78002" + }, + "spouse_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF SPOUSE-2020", + "variable": "ER81655" + }, + { + "label": "ACC MISC LABOR INCOME OF SPOUSE-2020", + "variable": "ER81669" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF SPOUSE-2020", + "variable": "ER81670" + } + }, + "2023": { + "head_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF RP-2022", + "variable": "ER85481" + }, + { + "label": "ACC MISC LABOR INCOME OF RP-2022", + "variable": "ER85495" + } + ], + "head_labor": { + "label": "LABOR INCOME OF REF PERSON-2022", + "variable": "ER85496" + }, + "interview": { + "label": "2023 FAMILY INTERVIEW (ID) NUMBER", + "variable": "ER82002" + }, + "spouse_accuracy": [ + { + "label": "ACC WAGES AND SALARIES OF SPOUSE-2022", + "variable": "ER85509" + }, + { + "label": "ACC MISC LABOR INCOME OF SPOUSE-2022", + "variable": "ER85523" + } + ], + "spouse_labor": { + "label": "LABOR INCOME OF SPOUSE-2022", + "variable": "ER85524" + } + } + }, + "individual": { + "by_wave": { + "1999": { + "age": { + "label": "AGE OF INDIVIDUAL 99", + "variable": "ER33504" + }, + "interview": { + "label": "1999 INTERVIEW NUMBER", + "variable": "ER33501" + }, + "relationship": { + "label": "RELATION TO HEAD 99", + "variable": "ER33503" + }, + "sequence": { + "label": "SEQUENCE NUMBER 99", + "variable": "ER33502" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 99", + "variable": "ER33547" + } + }, + "2001": { + "age": { + "label": "AGE OF INDIVIDUAL 01", + "variable": "ER33604" + }, + "interview": { + "label": "2001 INTERVIEW NUMBER", + "variable": "ER33601" + }, + "relationship": { + "label": "RELATION TO HEAD 01", + "variable": "ER33603" + }, + "sequence": { + "label": "SEQUENCE NUMBER 01", + "variable": "ER33602" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 01", + "variable": "ER33639" + } + }, + "2003": { + "age": { + "label": "AGE OF INDIVIDUAL 03", + "variable": "ER33704" + }, + "interview": { + "label": "2003 INTERVIEW NUMBER", + "variable": "ER33701" + }, + "relationship": { + "label": "RELATION TO HEAD 03", + "variable": "ER33703" + }, + "sequence": { + "label": "SEQUENCE NUMBER 03", + "variable": "ER33702" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 03", + "variable": "ER33742" + } + }, + "2005": { + "age": { + "label": "AGE OF INDIVIDUAL 05", + "variable": "ER33804" + }, + "interview": { + "label": "2005 INTERVIEW NUMBER", + "variable": "ER33801" + }, + "relationship": { + "label": "RELATION TO HEAD 05", + "variable": "ER33803" + }, + "sequence": { + "label": "SEQUENCE NUMBER 05", + "variable": "ER33802" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 05", + "variable": "ER33849" + } + }, + "2007": { + "age": { + "label": "AGE OF INDIVIDUAL 07", + "variable": "ER33904" + }, + "interview": { + "label": "2007 INTERVIEW NUMBER", + "variable": "ER33901" + }, + "relationship": { + "label": "RELATION TO HEAD 07", + "variable": "ER33903" + }, + "sequence": { + "label": "SEQUENCE NUMBER 07", + "variable": "ER33902" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 07", + "variable": "ER33951" + } + }, + "2009": { + "age": { + "label": "AGE OF INDIVIDUAL 09", + "variable": "ER34004" + }, + "interview": { + "label": "2009 INTERVIEW NUMBER", + "variable": "ER34001" + }, + "relationship": { + "label": "RELATION TO HEAD 09", + "variable": "ER34003" + }, + "sequence": { + "label": "SEQUENCE NUMBER 09", + "variable": "ER34002" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 09", + "variable": "ER34046" + } + }, + "2011": { + "age": { + "label": "AGE OF INDIVIDUAL 11", + "variable": "ER34104" + }, + "interview": { + "label": "2011 INTERVIEW NUMBER", + "variable": "ER34101" + }, + "relationship": { + "label": "RELATION TO HEAD 11", + "variable": "ER34103" + }, + "sequence": { + "label": "SEQUENCE NUMBER 11", + "variable": "ER34102" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 11", + "variable": "ER34155" + } + }, + "2013": { + "age": { + "label": "AGE OF INDIVIDUAL 13", + "variable": "ER34204" + }, + "interview": { + "label": "2013 INTERVIEW NUMBER", + "variable": "ER34201" + }, + "relationship": { + "label": "RELATION TO HEAD 13", + "variable": "ER34203" + }, + "sequence": { + "label": "SEQUENCE NUMBER 13", + "variable": "ER34202" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 13", + "variable": "ER34269" + } + }, + "2015": { + "age": { + "label": "AGE OF INDIVIDUAL 15", + "variable": "ER34305" + }, + "interview": { + "label": "2015 INTERVIEW NUMBER", + "variable": "ER34301" + }, + "relationship": { + "label": "RELATION TO HEAD 15", + "variable": "ER34303" + }, + "sequence": { + "label": "SEQUENCE NUMBER 15", + "variable": "ER34302" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 15", + "variable": "ER34414" + } + }, + "2017": { + "age": { + "label": "AGE OF INDIVIDUAL 17", + "variable": "ER34504" + }, + "interview": { + "label": "2017 INTERVIEW NUMBER", + "variable": "ER34501" + }, + "relationship": { + "label": "RELATION TO REFERENCE PERSON 17", + "variable": "ER34503" + }, + "sequence": { + "label": "SEQUENCE NUMBER 17", + "variable": "ER34502" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 17", + "variable": "ER34651" + } + }, + "2019": { + "age": { + "label": "AGE OF INDIVIDUAL 19", + "variable": "ER34704" + }, + "interview": { + "label": "2019 INTERVIEW NUMBER", + "variable": "ER34701" + }, + "relationship": { + "label": "RELATION TO REFERENCE PERSON 19", + "variable": "ER34703" + }, + "sequence": { + "label": "SEQUENCE NUMBER 19", + "variable": "ER34702" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 19", + "variable": "ER34864" + } + }, + "2021": { + "age": { + "label": "AGE OF INDIVIDUAL 21", + "variable": "ER34904" + }, + "interview": { + "label": "2021 INTERVIEW NUMBER", + "variable": "ER34901" + }, + "relationship": { + "label": "RELATION TO REFERENCE PERSON 21", + "variable": "ER34903" + }, + "sequence": { + "label": "SEQUENCE NUMBER 21", + "variable": "ER34902" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 21", + "variable": "ER35065" + } + }, + "2023": { + "age": { + "label": "AGE OF INDIVIDUAL 23", + "variable": "ER35104" + }, + "interview": { + "label": "2023 INTERVIEW NUMBER", + "variable": "ER35101" + }, + "relationship": { + "label": "RELATION TO REFERENCE PERSON 23", + "variable": "ER35103" + }, + "sequence": { + "label": "SEQUENCE NUMBER 23", + "variable": "ER35102" + }, + "weight": { + "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 23", + "variable": "ER35265" + } + } + }, + "concepts": { + "age": [ + "^AGE OF INDIVIDUAL" + ], + "interview": [ + "INTERVIEW NUMBER" + ], + "relationship": [ + "^RELATION(SHIP)? TO (HEAD|REFERENCE PERSON)" + ], + "sequence": [ + "^SEQUENCE NUMBER" + ], + "weight": [ + "^INDIVIDUAL WEIGHT|CROSS-SECTION WT", + "^CORE IND WEIGHT", + "LONGITUDINAL W" + ] + }, + "identifiers": { + "interview_1968": { + "label": "1968 INTERVIEW NUMBER", + "variable": "ER30001" + }, + "person_number": { + "label": "PERSON NUMBER 68", + "variable": "ER30002" + } + } + } + }, + "schema": "track_b_b0_2_counts.v1", + "script_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "worktree_clean": true +} diff --git a/docs/design/track_b_b0_2_counts_mutations.json b/docs/design/track_b_b0_2_counts_mutations.json new file mode 100644 index 00000000..ee236134 --- /dev/null +++ b/docs/design/track_b_b0_2_counts_mutations.json @@ -0,0 +1,3215 @@ +{ + "schema_version": 1, + "verification_class": "structural-count protocol conformance", + "admitted_scope": "Invented frames only; no real-data run.", + "mutation_method": "One deliberate production behavior change per check, from docs/design/track_b_b0_2_evidence/mutation_specs.json. Each check runs the whole count test module in a fresh pytest process with its own Hypothesis database; a nonzero exit kills it. The original source bytes are restored and their SHA-256 verified after every check. No mutated implementation is committed.", + "protocol_sha256": "6840f5fa41c897407c5896d4c3729ca5a5c0f1d166faa294e65e42309908ad23", + "restored_source_sha256": { + "scripts/track_b_b0_2_counts.py": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321" + }, + "result": "127/127 mutations killed", + "runner_command": "OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONDONTWRITEBYTECODE=1 .venv/bin/python docs/design/track_b_b0_2_evidence/mutations.py RECORD", + "test_command": "PYTHONPATH=$PWD/src:$PWD python -m pytest -q -rf -p no:cacheprovider tests/test_track_b_b0_2_counts.py", + "checks": [ + { + "mutation": "band edges", + "replacements": [ + { + "old": "OLDER_BANDS = ((\"62_64\", 62, 64), (\"65_66\", 65, 66), (\"67_69\", 67, 69))", + "new": "OLDER_BANDS = ((\"62_64\", 63, 64), (\"65_66\", 65, 66), (\"67_69\", 67, 69))", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_relation_the_rows_do_not_guarantee_is_not_registered", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_keep_nonempty_2009_origins", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_use_exact_lags_and_later_wave_band", + "tests/test_track_b_b0_2_counts.py::test_b3l_uses_2007_origin_and_fixed_origin_weights_for_later_support", + "tests/test_track_b_b0_2_counts.py::test_band_edges_and_b3_support_are_hand_counted", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]" + ], + "summary": "11 failed, 231 passed in 175.21s (0:02:55)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 175.8 + }, + { + "mutation": "lag set", + "replacements": [ + { + "old": "LAGS = (2, 4)", + "new": "LAGS = (2, 6)", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_keep_nonempty_2009_origins", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_use_exact_lags_and_later_wave_band", + "tests/test_track_b_b0_2_counts.py::test_counts_are_byte_invariant_to_valid_labor_values", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size" + ], + "summary": "4 failed, 238 passed in 192.53s (0:03:12)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 193.1 + }, + { + "mutation": "R_TA_PROXY age threshold", + "replacements": [ + { + "old": "proxy = proxy[_known_age(proxy[\"age\"]) & (proxy[\"age\"] >= 30)]", + "new": "proxy = proxy[_known_age(proxy[\"age\"]) & (proxy[\"age\"] > 30)]", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_rosters_and_age_30_proxy_boundary_use_their_own_weights_and_ages", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "20 failed, 222 passed in 104.03s (0:01:44)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 104.6 + }, + { + "mutation": "valid-row rule", + "replacements": [ + { + "old": "rows[\"valid\"], [\"person_id\", \"reference_year\", \"acc_flag\"]", + "new": "rows[\"weight_positive\"], [\"person_id\", \"reference_year\", \"acc_flag\"]", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_use_exact_lags_and_later_wave_band", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_main_stops_when_b0_1_counts_differ_from_the_inputs", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_outside_b2_excludes_entire_anchor_households_and_counts_later_units", + "tests/test_track_b_b0_2_counts.py::test_rosters_and_age_30_proxy_boundary_use_their_own_weights_and_ages", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program", + "tests/test_track_b_b0_2_counts.py::test_valid_rows_require_present_role_positive_weight_and_role_validity" + ], + "summary": "22 failed, 220 passed in 130.80s (0:02:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 131.4 + }, + { + "mutation": "B3L origin wave", + "replacements": [ + { + "old": "B3L_ORIGIN_WAVE = 2007", + "new": "B3L_ORIGIN_WAVE = 2009", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_b3l_uses_2007_origin_and_fixed_origin_weights_for_later_support" + ], + "summary": "2 failed, 240 passed in 105.79s (0:01:45)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 106.3 + }, + { + "mutation": "sensitive-quantity threshold changed from 10 to 9", + "replacements": [ + { + "old": "DISCLOSURE_THRESHOLD = 10", + "new": "DISCLOSURE_THRESHOLD = 9", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosed_target_stays_outside_one_to_nine", + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again", + "tests/test_track_b_b0_2_counts.py::test_only_a_narrow_target_of_true_size_one_to_nine_withholds[9]", + "tests/test_track_b_b0_2_counts.py::test_the_fixed_base_is_ten_with_no_ladder", + "tests/test_track_b_b0_2_counts.py::test_the_relaxation_proves_irrelevance_only_above_nine_and_a_half[9.0-narrow]", + "tests/test_track_b_b0_2_counts.py::test_the_relaxation_proves_irrelevance_only_above_nine_and_a_half[9.5-narrow]", + "tests/test_track_b_b0_2_counts.py::test_the_relaxation_proves_irrelevance_only_above_nine_and_a_half[None-narrow]" + ], + "summary": "12 failed, 230 passed in 129.48s (0:02:09)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 130.0 + }, + { + "mutation": "effective sizes left uncoarsened", + "replacements": [ + { + "old": "_put(published, path, coarsen(value, base))", + "new": "_put(published, path, value if path[-1].endswith(\"_n_eff\") else coarsen(value, base))", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_the_committed_file_is_identical_for_every_protected_remainder" + ], + "summary": "2 failed, 240 passed in 120.35s (0:02:00)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 121.0 + }, + { + "mutation": "local copy allowed inside repository", + "replacements": [ + { + "old": "raise SystemExit(\"local copy must be outside the repository\")", + "new": "pass # Deliberately permit repository-local copies.", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_refuses_local_copy_inside_repository_before_read[False]", + "tests/test_track_b_b0_2_counts.py::test_main_refuses_local_copy_inside_repository_before_read[True]", + "tests/test_track_b_b0_2_counts.py::test_main_refuses_local_copy_through_repository_symlink_before_read" + ], + "summary": "3 failed, 239 passed in 111.92s (0:01:51)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 112.5 + }, + { + "mutation": "coarsening rounds halves down", + "replacements": [ + { + "old": "return int((Decimal(str(value)) / base + Decimal(\"0.5\")) // 1) * base", + "new": "return int((Decimal(str(value)) / base + Decimal(\"0.5\")).to_integral_value(rounding=\"ROUND_CEILING\") - 1) * base", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosed_target_stays_outside_one_to_nine", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_a_narrow_target_outside_one_to_nine_starts_another_round", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-1]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-2]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-1]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-2]", + "tests/test_track_b_b0_2_counts.py::test_audit_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_audit_programs_depend_only_on_the_publication", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_coarsening_boundaries_round_halves_up[10-values0-expected0]", + "tests/test_track_b_b0_2_counts.py::test_coarsening_boundaries_round_halves_up[50-values1-expected1]", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_count_intervals_are_exact_preimages_of_coarsening[10]", + "tests/test_track_b_b0_2_counts.py::test_count_intervals_are_exact_preimages_of_coarsening[50]", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_effective_size_attack_registers_household_and_design_lower_bounds", + "tests/test_track_b_b0_2_counts.py::test_effective_sizes_are_coarsened_from_their_own_unrounded_values", + "tests/test_track_b_b0_2_counts.py::test_every_target_of_an_audit_stopped_before_any_round_is_unreached", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes3]", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_targets_are_classified_in_one_order_whatever_their_sizes", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]" + ], + "summary": "26 failed, 216 passed in 429.26s (0:07:09)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 430.2 + }, + { + "mutation": "effective-size lower bounds omitted", + "replacements": [ + { + "old": "for path, count_path in registry.effective_bounds.items():", + "new": "for path, count_path in (): # Deliberately omit effective bounds.", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_published_effective_size_lifts_its_count_interval" + ], + "summary": "1 failed, 241 passed in 102.88s (0:01:42)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 103.5 + }, + { + "mutation": "protection width reduced to 3", + "replacements": [ + { + "old": "PROTECTION_WIDTH = 4", + "new": "PROTECTION_WIDTH = 3", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_audit_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]", + "tests/test_track_b_b0_2_counts.py::test_the_fixed_base_is_ten_with_no_ladder" + ], + "summary": "10 failed, 232 passed in 107.82s (0:01:47)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 108.4 + }, + { + "mutation": "unknown age counted in 80 and over", + "replacements": [ + { + "old": "return _known_age(ages) & numeric.between(low, high)", + "new": "return (_known_age(ages) & numeric.between(low, high)) | (~_known_age(ages) & (low == 80))", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_known_age_partition_and_reader_maximum_are_hand_counted", + "tests/test_track_b_b0_2_counts.py::test_missing_999_and_zero_ages_through_actual_named_readers" + ], + "summary": "2 failed, 240 passed in 124.61s (0:02:04)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 125.2 + }, + { + "mutation": "frozen check removed", + "replacements": [ + { + "old": " _check_protocol()\n", + "new": " pass # Deliberately omit frozen protocol validation.\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_requires_frozen_pinned_protocol_before_any_input[draft]", + "tests/test_track_b_b0_2_counts.py::test_main_requires_frozen_pinned_protocol_before_any_input[pin]" + ], + "summary": "2 failed, 240 passed in 128.43s (0:02:08)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 129.0 + }, + { + "mutation": "published B interval starts at B/2", + "replacements": [ + { + "old": "return max(int(value - base // 2), base), int(value + base // 2 - 1)", + "new": "return int(value - base // 2), int(value + base // 2 - 1)", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_audit_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_coarsening_boundaries_round_halves_up[10-values0-expected0]", + "tests/test_track_b_b0_2_counts.py::test_coarsening_boundaries_round_halves_up[50-values1-expected1]", + "tests/test_track_b_b0_2_counts.py::test_count_intervals_are_exact_preimages_of_coarsening[10]", + "tests/test_track_b_b0_2_counts.py::test_count_intervals_are_exact_preimages_of_coarsening[50]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes3]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes4]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]" + ], + "summary": "11 failed, 231 passed in 120.73s (0:02:00)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 121.3 + }, + { + "mutation": "80-and-over band ends at 80", + "replacements": [ + { + "old": "(\"80_plus\", 80, age67._MAX_AGE_CODE),", + "new": "(\"80_plus\", 80, 80),", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_band_edges_and_b3_support_are_hand_counted", + "tests/test_track_b_b0_2_counts.py::test_known_age_partition_and_reader_maximum_are_hand_counted", + "tests/test_track_b_b0_2_counts.py::test_missing_999_and_zero_ages_through_actual_named_readers" + ], + "summary": "3 failed, 239 passed in 196.50s (0:03:16)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 197.1 + }, + { + "mutation": "2009 lag origins dropped", + "replacements": [ + { + "old": "if later not in WAVES:", + "new": "if later not in WAVES or wave == 2009:", + "occurrence": 1, + "anchor_occurrences": 2 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_keep_nonempty_2009_origins", + "tests/test_track_b_b0_2_counts.py::test_b3_lag_pairs_use_exact_lags_and_later_wave_band", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_disclosure_integer_hidden_band_attack_is_blocked_on_full_release", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_disclosure_registry_covers_all_statistics_and_correct_unit_types", + "tests/test_track_b_b0_2_counts.py::test_disclosure_unpublished_coresident_remainder_is_audited", + "tests/test_track_b_b0_2_counts.py::test_effective_size_attack_registers_household_and_design_lower_bounds", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_missing_999_and_zero_ages_through_actual_named_readers", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_committed_file_is_identical_for_every_protected_remainder", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "39 failed, 203 passed in 250.89s (0:04:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 251.4 + }, + { + "mutation": "outside-B2 household exclusion", + "replacements": [ + { + "old": "outside = full[~full[\"interview\"].isin(domain[\"interview\"])]", + "new": "outside = full[~full[\"person_id\"].isin(domain[\"person_id\"])]", + "occurrence": "all", + "anchor_occurrences": 2 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_disclosure_unpublished_coresident_remainder_is_audited", + "tests/test_track_b_b0_2_counts.py::test_outside_b2_excludes_entire_anchor_households_and_counts_later_units", + "tests/test_track_b_b0_2_counts.py::test_the_committed_file_is_identical_for_every_protected_remainder" + ], + "summary": "3 failed, 239 passed in 112.22s (0:01:52)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 112.8 + }, + { + "mutation": "attempt record not checked, allowing a silent retry", + "replacements": [ + { + "old": "if attempt.exists() or attempt.is_symlink():", + "new": "if False and (attempt.exists() or attempt.is_symlink()):", + "occurrence": 1, + "anchor_occurrences": 1 + }, + { + "old": "with attempt.open(\"x\", encoding=\"utf-8\") as stream:", + "new": "with attempt.open(\"w\", encoding=\"utf-8\") as stream:", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failure_from_outside_this_script_prints_no_value", + "tests/test_track_b_b0_2_counts.py::test_attempt_record_survives_failed_start_and_refuses_retry", + "tests/test_track_b_b0_2_counts.py::test_main_refuses_attempt_record_through_existing_symlink" + ], + "summary": "3 failed, 239 passed in 102.07s (0:01:42)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 102.6 + }, + { + "mutation": "effective bound for a published B starts at B/2", + "replacements": [ + { + "old": "return max(base, math.ceil(value - base / 2))", + "new": "return max(0, math.ceil(value - base / 2))", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_published_effective_size_lifts_its_count_interval", + "tests/test_track_b_b0_2_counts.py::test_an_effective_size_bounds_its_count_from_below[10-10]" + ], + "summary": "2 failed, 240 passed in 165.73s (0:02:45)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 166.3 + }, + { + "mutation": "audit skips published subset differences", + "replacements": [ + { + "old": "if superset == subset or keys[superset][0] != keys[subset][0]:", + "new": "if True:", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_disclosure_audits_multi_atom_published_subset_differences", + "tests/test_track_b_b0_2_counts.py::test_earlier_sets_refine_atoms_but_set_no_targets_of_their_own", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_class_sizes_are_this_releases_classes_alone", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts", + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "12 failed, 230 passed in 124.42s (0:02:04)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 124.9 + }, + { + "mutation": "continuous relaxation replaces integer programs", + "replacements": [ + { + "old": "integrality=np.full(self.variables, int(integral)),", + "new": "integrality=np.zeros(self.variables),", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosed_target_stays_outside_one_to_nine", + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes0]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes1]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes2]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes3]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes4]" + ], + "summary": "10 failed, 232 passed in 117.07s (0:01:57)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 117.6 + }, + { + "mutation": "pool seeded with the true sizes", + "replacements": [ + { + "old": " pool = WitnessPool(len(truth))\n", + "new": " pool = WitnessPool(len(truth))\n pool.add(truth)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_an_undetermined_sensitive_target_withholds", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again", + "tests/test_track_b_b0_2_counts.py::test_every_target_of_an_audit_stopped_before_any_round_is_unreached", + "tests/test_track_b_b0_2_counts.py::test_unanswered_programs_withhold" + ], + "summary": "5 failed, 237 passed in 130.03s (0:02:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 130.5 + }, + { + "mutation": "targets taken in order of true size", + "replacements": [ + { + "old": " for index in range(len(targets)):\n if index in disclosed:", + "new": " for index in sorted(range(len(targets)), key=lambda i: targets[i][\"true_size\"]):\n if index in disclosed:", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-1]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_targets_are_classified_in_one_order_whatever_their_sizes" + ], + "summary": "5 failed, 237 passed in 116.17s (0:01:56)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 116.6 + }, + { + "mutation": "search windows centred on the true size", + "replacements": [ + { + "old": " low, high = pool.values(columns)\n", + "new": " low, high = pool.values(columns)\n low = high = target[\"true_size\"]\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_target_the_pool_shows_wide_runs_no_program", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_audit_programs_depend_only_on_the_publication", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again", + "tests/test_track_b_b0_2_counts.py::test_programs_with_earlier_counts_depend_only_on_the_publications", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "6 failed, 236 passed in 159.22s (0:02:39)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 159.8 + }, + { + "mutation": "disclosure test widened to sizes 2-9", + "replacements": [ + { + "old": "if 1 <= targets[index][\"true_size\"] < DISCLOSURE_THRESHOLD:", + "new": "if 2 <= targets[index][\"true_size\"] < DISCLOSURE_THRESHOLD:", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_undetermined_sensitive_target_withholds", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]" + ], + "summary": "6 failed, 236 passed in 129.71s (0:02:09)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 130.3 + }, + { + "mutation": "an empty pool releases", + "replacements": [ + { + "old": " # No program found a point, so nothing can be shown wide.\n status = \"withheld\"", + "new": " # No program found a point, so nothing can be shown wide.\n pass", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_every_target_of_an_audit_stopped_before_any_round_is_unreached" + ], + "summary": "1 failed, 241 passed in 206.16s (0:03:26)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 206.8 + }, + { + "mutation": "a single round, with no reclassification", + "replacements": [ + { + "old": " if not added:\n break\n", + "new": " break\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_narrow_target_outside_one_to_nine_starts_another_round", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-2]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-2]", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again" + ], + "summary": "8 failed, 234 passed in 105.26s (0:01:45)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 105.8 + }, + { + "mutation": "disclosed targets left unconstrained", + "replacements": [ + { + "old": "self.disclosed = [tuple(target[\"columns\"]) for target in disclosed]", + "new": "self.disclosed = []", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosed_target_stays_outside_one_to_nine", + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_narrow_target_outside_one_to_nine_starts_another_round", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-2]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-2]", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again" + ], + "summary": "9 failed, 233 passed in 157.69s (0:02:37)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 158.2 + }, + { + "mutation": "pool keeps points a disclosure rules out", + "replacements": [ + { + "old": " pool.keep(system)\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_narrow_target_outside_one_to_nine_starts_another_round", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again" + ], + "summary": "3 failed, 239 passed in 147.76s (0:02:27)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 148.3 + }, + { + "mutation": "relaxation margin loosened to 8.5", + "replacements": [ + { + "old": "relaxed > DISCLOSURE_THRESHOLD - 0.5", + "new": "relaxed > DISCLOSURE_THRESHOLD - 1.5", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_relaxation_proves_irrelevance_only_above_nine_and_a_half[9.0-narrow]", + "tests/test_track_b_b0_2_counts.py::test_the_relaxation_proves_irrelevance_only_above_nine_and_a_half[9.5-narrow]" + ], + "summary": "2 failed, 240 passed in 109.14s (0:01:49)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 109.7 + }, + { + "mutation": "a timed-out search proves absence", + "replacements": [ + { + "old": "proven_absent = proven_absent and status == 2", + "new": "proven_absent = proven_absent and status in (1, 2)", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_timed_out_search_proves_nothing[statuses0]", + "tests/test_track_b_b0_2_counts.py::test_a_timed_out_search_proves_nothing[statuses1]", + "tests/test_track_b_b0_2_counts.py::test_a_timed_out_search_proves_nothing[statuses2]" + ], + "summary": "3 failed, 239 passed in 185.53s (0:03:05)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 186.1 + }, + { + "mutation": "solver witnesses used without the exact check", + "replacements": [ + { + "old": " if not self.feasible(sizes):\n return None\n", + "new": " if False:\n return None\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_witnesses_are_checked_exactly_and_never_taken_from_the_solver" + ], + "summary": "1 failed, 241 passed in 105.30s (0:01:45)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 105.9 + }, + { + "mutation": "positive solver gap", + "replacements": [ + { + "old": "\"mip_rel_gap\": gap,", + "new": "\"mip_rel_gap\": 0.01,", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_programs_use_one_thread_the_time_limit_and_exact_optima" + ], + "summary": "1 failed, 241 passed in 176.84s (0:02:56)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 177.5 + }, + { + "mutation": "atoms assumed nonempty", + "replacements": [ + { + "old": " self.bounds = Bounds(\n 0,", + "new": " self.bounds = Bounds(\n np.concatenate([np.ones(self.atoms), np.zeros(binaries)]),", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes0]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes1]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes2]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes3]", + "tests/test_track_b_b0_2_counts.py::test_integer_audit_extrema_agree_with_bruteforce[sizes4]", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[False]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]", + "tests/test_track_b_b0_2_counts.py::test_the_committed_file_is_identical_for_every_protected_remainder", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]" + ], + "summary": "23 failed, 219 passed in 109.67s (0:01:49)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 110.2 + }, + { + "mutation": "relations registered whether or not guaranteed", + "replacements": [ + { + "old": " if guaranteed:\n registry.relate(", + "new": " if True:\n registry.relate(", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_relation_the_rows_do_not_guarantee_is_not_registered" + ], + "summary": "1 failed, 241 passed in 290.25s (0:04:50)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 290.8 + }, + { + "mutation": "a violated relation tolerated", + "replacements": [ + { + "old": " raise ValueError(f\"a guaranteed relation fails: {kind} {left}\")", + "new": " continue", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_violated_relation_is_a_defect_not_data" + ], + "summary": "1 failed, 241 passed in 101.94s (0:01:41)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 102.4 + }, + { + "mutation": "relations dropped from the audit", + "replacements": [ + { + "old": " relations = model_relations(registry, model)\n", + "new": " relations = model_relations(registry, model)[:0]\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]" + ], + "summary": "3 failed, 239 passed in 207.91s (0:03:27)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 208.3 + }, + { + "mutation": "public block reports the audited target count", + "replacements": [ + { + "old": " \"status\": audit[\"status\"],\n }", + "new": " \"status\": audit[\"status\"],\n \"audited\": len(audit[\"targets\"]),\n }", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_withheld_release_publishes_status_and_no_counts", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_the_committed_file_is_identical_for_every_protected_remainder" + ], + "summary": "7 failed, 235 passed in 166.97s (0:02:46)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 167.4 + }, + { + "mutation": "fractional ages accepted", + "replacements": [ + { + "old": "numeric.between(1, age67._MAX_AGE_CODE) & numeric.mod(1).eq(0)", + "new": "numeric.between(1, age67._MAX_AGE_CODE)", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_known_age_partition_and_reader_maximum_are_hand_counted" + ], + "summary": "1 failed, 241 passed in 94.09s (0:01:34)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 94.5 + }, + { + "mutation": "negative coarsening input accepted", + "replacements": [ + { + "old": "if value < 0 or not np.isfinite(value):", + "new": "if not np.isfinite(value):", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_coarsening_refuses_negative_and_nonfinite_values[-0.5]", + "tests/test_track_b_b0_2_counts.py::test_coarsening_refuses_negative_and_nonfinite_values[-1]", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[-7-reading the inputs-^B0\\\\.2 stopped in coarsen, line \\\\d+: ValueError: counts and effective sizes must be finite and nonnegative$]", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[-7-the release-^B0\\\\.2 stopped in coarsen, line \\\\d+: ValueError: counts and effective sizes must be finite and nonnegative$]" + ], + "summary": "4 failed, 238 passed in 115.36s (0:01:55)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 115.9 + }, + { + "mutation": "nonfinite coarsening input accepted", + "replacements": [ + { + "old": "if value < 0 or not np.isfinite(value):", + "new": "if value < 0:", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_coarsening_refuses_negative_and_nonfinite_values[inf]", + "tests/test_track_b_b0_2_counts.py::test_coarsening_refuses_negative_and_nonfinite_values[nan]" + ], + "summary": "2 failed, 240 passed in 191.80s (0:03:11)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 192.3 + }, + { + "mutation": "output replaces an existing file", + "replacements": [ + { + "old": " os.link(temporary, path)\n", + "new": " os.replace(temporary, path)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_write_new_leaves_no_file_under_its_name_on_failure[link]", + "tests/test_track_b_b0_2_counts.py::test_write_new_writes_every_byte_and_refuses_an_existing_name" + ], + "summary": "4 failed, 238 passed in 100.07s (0:01:40)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 100.5 + }, + { + "mutation": "output not flushed to disk", + "replacements": [ + { + "old": " view = view[os.write(descriptor, view) :]\n os.fsync(descriptor)\n", + "new": " view = view[os.write(descriptor, view) :]\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_write_new_flushes_the_directory_after_linking", + "tests/test_track_b_b0_2_counts.py::test_write_new_leaves_no_file_under_its_name_on_failure[fsync]" + ], + "summary": "2 failed, 240 passed in 179.95s (0:02:59)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 180.5 + }, + { + "mutation": "a short write left incomplete", + "replacements": [ + { + "old": " while view:\n view = view[os.write(descriptor, view) :]\n", + "new": " os.write(descriptor, view)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_write_new_completes_short_writes" + ], + "summary": "1 failed, 241 passed in 248.57s (0:04:08)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 249.9 + }, + { + "mutation": "attempt record not flushed to disk", + "replacements": [ + { + "old": " stream.flush()\n os.fsync(stream.fileno())\n", + "new": " stream.flush()\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_attempt_record_is_flushed_to_disk" + ], + "summary": "1 failed, 241 passed in 160.69s (0:02:40)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 161.2 + }, + { + "mutation": "B0.1's sets left out of the model", + "replacements": [ + { + "old": " for path, (unit_type, units, _) in registry.prior.items():\n grouped.setdefault((unit_type, units), []).append(path)\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[nested]", + "tests/test_track_b_b0_2_counts.py::test_earlier_sets_refine_atoms_but_set_no_targets_of_their_own", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_programs_with_earlier_counts_depend_only_on_the_publications", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[False]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[falling]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[late]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[rising]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_is_the_same_whatever_the_rounds", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "47 failed, 195 passed in 964.08s (0:16:04)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 965.2 + }, + { + "mutation": "B0.1's counts bound their sets only from below", + "replacements": [ + { + "old": " lower[row] = max(lower[row], value)\n upper[row] = min(upper[row], value)\n", + "new": " lower[row] = max(lower[row], value)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_earlier_sets_refine_atoms_but_set_no_targets_of_their_own", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_audit_uses_the_relations_it_registers[True]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]" + ], + "summary": "17 failed, 225 passed, 148 warnings in 190.34s (0:03:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 190.9 + }, + { + "mutation": "targets are the refined atoms, not this release's classes", + "replacements": [ + { + "old": " rows = tuple(row for row in signature if current[row])\n", + "new": " rows = tuple(signature)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[household]", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[nested]", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[release_panel]", + "tests/test_track_b_b0_2_counts.py::test_earlier_sets_refine_atoms_but_set_no_targets_of_their_own", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts", + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "15 failed, 227 passed in 161.26s (0:02:41)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 161.9 + }, + { + "mutation": "differences of two B0.1 sets audited as targets", + "replacements": [ + { + "old": " and not (current[subset] and current[superset])\n ):\n", + "new": " and False\n ):\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_earlier_sets_refine_atoms_but_set_no_targets_of_their_own", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]" + ], + "summary": "3 failed, 239 passed in 281.00s (0:04:41)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 281.5 + }, + { + "mutation": "a B0.1 count not checked against its set", + "replacements": [ + { + "old": " if value != len(units):\n", + "new": " if False:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_earlier_count_must_be_its_sets_size" + ], + "summary": "1 failed, 241 passed in 200.11s (0:03:20)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 200.6 + }, + { + "mutation": "B0.1's rerun not compared with its record", + "replacements": [ + { + "old": " if rerun != counts:\n", + "new": " if False:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_stops_when_b0_1_counts_differ_from_the_inputs", + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give" + ], + "summary": "2 failed, 240 passed in 142.59s (0:02:22)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 143.1 + }, + { + "mutation": "B0.1's sums not checked", + "replacements": [ + { + "old": " if value != sum(leaves[part] for part in sums[path]):\n", + "new": " if False:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give" + ], + "summary": "1 failed, 241 passed in 180.22s (0:03:00)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 180.8 + }, + { + "mutation": "a modeled set B0.1 never published accepted", + "replacements": [ + { + "old": " if missing := set(sets) - set(leaves):\n", + "new": " if missing := set():\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give" + ], + "summary": "1 failed, 241 passed in 274.48s (0:04:34)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 275.0 + }, + { + "mutation": "B0.1's ledger copies not checked", + "replacements": [ + { + "old": " if copied != expected:\n", + "new": " if False:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give" + ], + "summary": "1 failed, 241 passed in 421.41s (0:07:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 422.0 + }, + { + "mutation": "a change pair's row count registered with its persons added", + "replacements": [ + { + "old": " value = sum(sign * _cell(counts, part) for sign, part in terms)\n", + "new": " value = sum(_cell(counts, part) for sign, part in terms)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "25 failed, 217 passed in 334.08s (0:05:34)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 334.6 + }, + { + "mutation": "B0.1's sets not registered in main()", + "replacements": [ + { + "old": " register_b0_1(registry, earlier, committed[\"counts\"])\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]" + ], + "summary": "7 failed, 235 passed in 257.46s (0:04:17)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 257.9 + }, + { + "mutation": "B0.1's record not checked before the audit", + "replacements": [ + { + "old": " check_b0_1_record(\n committed,\n b0_1_structural_counts(person_wave, validity_by_wave),\n earlier,\n )\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_stops_at_the_rerun_gate_before_any_count_is_written", + "tests/test_track_b_b0_2_counts.py::test_main_stops_when_b0_1_counts_differ_from_the_inputs" + ], + "summary": "2 failed, 240 passed in 319.69s (0:05:19)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 320.2 + }, + { + "mutation": "B0.1's age support ends a year early", + "replacements": [ + { + "old": " in_support=valid[\"age\"].between(low, high).to_numpy(),\n", + "new": " in_support=valid[\"age\"].between(low, high - 1).to_numpy(),\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_b0_1_sets_are_the_sets_b0_1_counts", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]" + ], + "summary": "13 failed, 229 passed in 137.30s (0:02:17)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 137.9 + }, + { + "mutation": "B0.1's dispositions inverted", + "replacements": [ + { + "old": " [person for person, label in labels.items() if label == name],\n", + "new": " [person for person, label in labels.items() if label != name],\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_failure_from_outside_this_script_prints_no_value", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[5-splits1]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_counts_enter_the_audit_through_main[97-splits0]", + "tests/test_track_b_b0_2_counts.py::test_b0_1_sets_are_the_sets_b0_1_counts", + "tests/test_track_b_b0_2_counts.py::test_b0_1_sets_on_a_hand_counted_example", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "33 failed, 209 passed in 241.83s (0:04:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 242.8 + }, + { + "mutation": "an empty pool classified as wide", + "replacements": [ + { + "old": " if pool.size == 0:\n return {**record, \"result\": \"narrow\", \"proof\": \"undetermined\"}, None\n", + "new": " if pool.size == 0:\n return {**record, \"result\": \"wide\", \"proof\": \"pool\"}, None\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_pool_proves_nothing_and_runs_no_program" + ], + "summary": "1 failed, 241 passed in 104.47s (0:01:44)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 104.9 + }, + { + "mutation": "an emptied pool not reseeded", + "replacements": [ + { + "old": " if reseeded:\n # The disclosures ruled out every point: seed again under them.\n seed_pool(system, pool, seconds=seconds)\n", + "new": " if reseeded:\n # The disclosures ruled out every point: seed again under them.\n pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_disclosures_that_empty_the_pool_seed_it_again" + ], + "summary": "2 failed, 240 passed in 87.37s (0:01:27)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 87.8 + }, + { + "mutation": "an empty reseeding releases", + "replacements": [ + { + "old": " seed_pool(system, pool, seconds=seconds)\n if pool.size == 0:\n status = \"withheld\"\n", + "new": " seed_pool(system, pool, seconds=seconds)\n if pool.size == 0:\n status = \"released\"\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files" + ], + "summary": "1 failed, 241 passed in 96.17s (0:01:36)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 96.5 + }, + { + "mutation": "seed directions drawn without the fixed seed", + "replacements": [ + { + "old": " generator = np.random.default_rng(POOL_SEED)\n", + "new": " generator = np.random.default_rng()\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_audit_programs_depend_only_on_the_publication", + "tests/test_track_b_b0_2_counts.py::test_main_records_provenance_and_checks_b01_size", + "tests/test_track_b_b0_2_counts.py::test_programs_with_earlier_counts_depend_only_on_the_publications", + "tests/test_track_b_b0_2_counts.py::test_seed_directions_come_from_the_fixed_seed", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "7 failed, 235 passed in 178.66s (0:02:58)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 179.1 + }, + { + "mutation": "targets involving B0.1's sets marked as this release's", + "replacements": [ + { + "old": " \"earlier\": not (current[superset] and current[subset]),\n", + "new": " \"earlier\": False,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts", + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "6 failed, 236 passed in 112.06s (0:01:52)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 112.8 + }, + { + "mutation": "the local copy writes atom columns", + "replacements": [ + { + "old": " {key: target[key] for key in TARGET_FIELDS if key in target}\n", + "new": " {key: target[key] for key in (*TARGET_FIELDS, \"columns\") if key in target}\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts" + ], + "summary": "5 failed, 237 passed in 410.82s (0:06:50)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 411.9 + }, + { + "mutation": "the local copy sums every target into class sizes", + "replacements": [ + { + "old": " if target[\"kind\"] == \"atom\":\n class_sizes.setdefault(", + "new": " if True:\n class_sizes.setdefault(", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_class_sizes_are_this_releases_classes_alone" + ], + "summary": "1 failed, 241 passed in 131.54s (0:02:11)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 132.0 + }, + { + "mutation": "B0.1's households taken at each row's wave", + "replacements": [ + { + "old": " home[person] for person in persons if pd.notna(home[person])\n", + "new": " person for person in persons if pd.notna(home[person])\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_b0_1_sets_are_the_sets_b0_1_counts", + "tests/test_track_b_b0_2_counts.py::test_b0_1_sets_on_a_hand_counted_example", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_main_allows_relative_local_copy_resolving_outside_repository", + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only", + "tests/test_track_b_b0_2_counts.py::test_the_b0_1_record_must_be_the_one_these_inputs_give", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "20 failed, 222 passed in 178.35s (0:02:58)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 178.8 + }, + { + "mutation": "the local copy writes the atom sizes", + "replacements": [ + { + "old": " \"class_sizes\": class_sizes,\n }\n", + "new": " \"class_sizes\": class_sizes,\n \"atom_sizes\": audit[\"atom_sizes\"],\n }\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_disclosure_public_block_holds_only_base_rules_and_status", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only" + ], + "summary": "7 failed, 235 passed in 195.28s (0:03:15)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 195.8 + }, + { + "mutation": "the audit prints the atom sizes", + "replacements": [ + { + "old": " truth = model[\"sizes\"]\n", + "new": " truth = model[\"sizes\"]\n print(truth.tolist(), file=sys.stderr)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_every_output_the_script_can_make_names_no_value" + ], + "summary": "1 failed, 241 passed in 151.33s (0:02:31)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 151.9 + }, + { + "mutation": "a failure keeps a library's message", + "replacements": [ + { + "old": " return f\"B0.2 stopped in {where}: {type(error).__name__}\"\n", + "new": " return f\"B0.2 stopped in {where}: {type(error).__name__}: {error}\"\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failure_while_reading_the_inputs_keeps_its_type_and_line", + "tests/test_track_b_b0_2_counts.py::test_an_interruption_or_a_librarys_exit_prints_no_chain[-reading the inputs-^B0\\\\.2 stopped in .+, line \\\\d+: (KeyboardInterrupt|SystemExit)$]", + "tests/test_track_b_b0_2_counts.py::test_an_interruption_or_a_librarys_exit_prints_no_chain[KeyboardInterrupt-reading the inputs-^B0\\\\.2 stopped in .+, line \\\\d+: (KeyboardInterrupt|SystemExit)$]", + "tests/test_track_b_b0_2_counts.py::test_attempt_record_survives_failed_start_and_refuses_retry", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_a_local_copy_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_never_overwrites_an_output_that_appears_during_read", + "tests/test_track_b_b0_2_counts.py::test_main_public_write_failure_keeps_complete_local_copy_and_attempt", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[7-reading the inputs-^B0\\\\.2 stopped in coarsen, line \\\\d+: TypeError$]", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[7-writing the outputs-^B0\\\\.2 stopped in coarsen, line \\\\d+: TypeError$]" + ], + "summary": "9 failed, 233 passed in 121.94s (0:02:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 122.4 + }, + { + "mutation": "warnings shown inside the guard", + "replacements": [ + { + "old": " warnings.simplefilter(\"ignore\")\n yield\n", + "new": " warnings.simplefilter(\"default\")\n yield\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_warnings_inside_the_guard_are_dropped_unseen" + ], + "summary": "1 failed, 241 passed in 209.31s (0:03:29)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 209.8 + }, + { + "mutation": "the solver prints its log", + "replacements": [ + { + "old": " \"disp\": False,\n", + "new": " \"disp\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_programs_use_one_thread_the_time_limit_and_exact_optima" + ], + "summary": "1 failed, 241 passed in 215.64s (0:03:35)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 216.0 + }, + { + "mutation": "the rerun gate skips the rebuilt sets' sizes", + "replacements": [ + { + "old": " if len(units) != sum(sign * leaves[part] for sign, part in terms):\n", + "new": " if False:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_stops_at_the_rerun_gate_before_any_count_is_written", + "tests/test_track_b_b0_2_counts.py::test_the_rerun_gate_stops_on_a_rebuilt_set_that_is_not_its_count" + ], + "summary": "2 failed, 240 passed in 181.94s (0:03:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 182.4 + }, + { + "mutation": "differences found in row order, not this release's first", + "replacements": [ + { + "old": " pairs.sort(key=lambda pair: not (current[pair[0]] and current[pair[1]]))\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "1 failed, 241 passed in 248.32s (0:04:08)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 248.8 + }, + { + "mutation": "two of this release's sets that B0.1 also published make no target", + "replacements": [ + { + "old": " and earlier[superset]\n and not (current[subset] and current[superset])\n ):\n", + "new": " and earlier[superset]\n ):\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "3 failed, 239 passed in 242.42s (0:04:02)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 242.9 + }, + { + "mutation": "the program limit back at 60 seconds", + "replacements": [ + { + "old": "PROGRAM_SECONDS = 300.0\n", + "new": "PROGRAM_SECONDS = 60.0\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_programs_use_one_thread_the_time_limit_and_exact_optima" + ], + "summary": "1 failed, 241 passed in 108.59s (0:01:48)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 109.0 + }, + { + "mutation": "the guard catches only Exception", + "replacements": [ + { + "old": " except BaseException as error:\n failure = _failure_text(error, phase)\n", + "new": " except Exception as error:\n failure = _failure_text(error, phase)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 2, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "7 failed, 219 passed in 89.14s (0:01:29)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 89.7 + }, + { + "mutation": "the local copy writes the rounds", + "replacements": [ + { + "old": " \"status\": audit[\"status\"],\n \"targets\": targets,\n", + "new": " \"status\": audit[\"status\"],\n \"rounds\": audit[\"rounds\"],\n \"targets\": targets,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_empty_reseeding_withholds_with_complete_files", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_is_the_same_whatever_the_rounds", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_writes_nothing_of_b0_1_sets_beyond_their_counts" + ], + "summary": "4 failed, 238 passed in 228.90s (0:03:48)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 229.4 + }, + { + "mutation": "the local copy writes each target's settling round", + "replacements": [ + { + "old": " \"true_size\",\n \"result\",\n)\n", + "new": " \"true_size\",\n \"result\",\n \"round\",\n)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-1]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[irrelevant-2]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-1]", + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-2]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[falling]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[late]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[rising]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_is_the_same_whatever_the_rounds" + ], + "summary": "9 failed, 233 passed in 118.79s (0:01:58)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 119.3 + }, + { + "mutation": "provenance gains a figure from the panel", + "replacements": [ + { + "old": " \"worktree_clean\": True,\n", + "new": " \"worktree_clean\": True,\n \"rows_read\": len(person_wave),\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_committed_provenance_is_the_requested_shape_and_input_hashes", + "tests/test_track_b_b0_2_counts.py::test_main_withheld_release_commits_provenance_and_status_only" + ], + "summary": "2 failed, 240 passed in 120.33s (0:02:00)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 120.8 + }, + { + "mutation": "a failure in the release prints its type and line", + "replacements": [ + { + "old": " if phase not in LOCATED_PHASES:\n return f\"B0.2 stopped in {RELEASE}\"\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failure_deep_in_the_disposition_rebuild_names_the_phase_alone", + "tests/test_track_b_b0_2_counts.py::test_a_failure_from_outside_this_script_prints_no_value", + "tests/test_track_b_b0_2_counts.py::test_an_interruption_or_a_librarys_exit_prints_no_chain[-the release-^B0\\\\.2 stopped in the release$]", + "tests/test_track_b_b0_2_counts.py::test_an_interruption_or_a_librarys_exit_prints_no_chain[KeyboardInterrupt-the release-^B0\\\\.2 stopped in the release$]", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[True]", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[7-an unlisted phase-^B0\\\\.2 stopped in the release$]", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[7-the release-^B0\\\\.2 stopped in the release$]" + ], + "summary": "7 failed, 235 passed in 176.47s (0:02:56)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 177.0 + }, + { + "mutation": "the guard skips the descriptor redirect", + "replacements": [ + { + "old": " os.dup2(null, 1)\n os.dup2(null, 2)\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failure_deep_in_the_disposition_rebuild_names_the_phase_alone", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[False]", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[True]" + ], + "summary": "3 failed, 239 passed in 194.78s (0:03:14)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 195.3 + }, + { + "mutation": "the guard skips the C library's flush", + "replacements": [ + { + "old": " ctypes.CDLL(None).fflush(None)\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_every_output_the_script_can_make_names_no_value", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[False]", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[True]" + ], + "summary": "3 failed, 239 passed in 167.78s (0:02:47)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 168.3 + }, + { + "mutation": "the guard leaves Python's streams unredirected", + "replacements": [ + { + "old": " with redirect_stdout(python_null), redirect_stderr(python_null):\n yield\n", + "new": " yield\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[False]", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[True]" + ], + "summary": "2 failed, 240 passed in 187.72s (0:03:07)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 188.2 + }, + { + "mutation": "the guard restores the descriptors before flushing", + "replacements": [ + { + "old": " try:\n _flush_streams()\n finally:\n os.dup2(saved[0], 1)\n os.dup2(saved[1], 2)\n", + "new": " try:\n os.dup2(saved[0], 1)\n os.dup2(saved[1], 2)\n finally:\n _flush_streams()\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[False]", + "tests/test_track_b_b0_2_counts.py::test_nothing_printed_inside_the_guard_is_seen[True]" + ], + "summary": "2 failed, 240 passed in 99.79s (0:01:39)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 100.3 + }, + { + "mutation": "main runs the release in the reading phase", + "replacements": [ + { + "old": " with count_free_failures(RELEASE):\n", + "new": " with count_free_failures(READING):\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failure_deep_in_the_disposition_rebuild_names_the_phase_alone", + "tests/test_track_b_b0_2_counts.py::test_a_failure_from_outside_this_script_prints_no_value", + "tests/test_track_b_b0_2_counts.py::test_every_output_the_script_can_make_names_no_value" + ], + "summary": "3 failed, 239 passed in 121.73s (0:02:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 122.1 + }, + { + "mutation": "the script's own raise in the release loses its message", + "replacements": [ + { + "old": " if raised_here:\n where", + "new": " if raised_here and phase in LOCATED_PHASES:\n where", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_main_rejects_b01_size_mismatch", + "tests/test_track_b_b0_2_counts.py::test_main_stops_at_the_rerun_gate_before_any_count_is_written", + "tests/test_track_b_b0_2_counts.py::test_main_stops_when_b0_1_counts_differ_from_the_inputs", + "tests/test_track_b_b0_2_counts.py::test_only_this_scripts_own_raise_keeps_its_message[-7-the release-^B0\\\\.2 stopped in coarsen, line \\\\d+: ValueError: counts and effective sizes must be finite and nonnegative$]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "11 failed, 231 passed in 171.72s (0:02:51)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 172.3 + }, + { + "mutation": "the load check reads the one-minute load", + "replacements": [ + { + "old": " os.getloadavg()[1],\n", + "new": " os.getloadavg()[0],\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_host_state_reads_niceness_darwin_priority_qos_load_and_cpus" + ], + "summary": "1 failed, 241 passed in 127.05s (0:02:07)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 127.6 + }, + { + "mutation": "the AC power check is dropped", + "replacements": [ + { + "old": " \"ac_power\": self.host.power_source() == AC_POWER,\n", + "new": " \"ac_power\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host0-ac_power]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host1-ac_power]" + ], + "summary": "4 failed, 238 passed in 141.89s (0:02:21)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 142.4 + }, + { + "mutation": "the sleep-assertion check is dropped", + "replacements": [ + { + "old": " \"sleep_assertions\": all(\n", + "new": " \"sleep_assertions\": True or all(\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host2-sleep_assertions]" + ], + "summary": "3 failed, 239 passed in 195.17s (0:03:15)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 195.6 + }, + { + "mutation": "the nice check is dropped", + "replacements": [ + { + "old": " \"nice\": niceness == 0,\n", + "new": " \"nice\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host3-nice]" + ], + "summary": "3 failed, 239 passed in 206.96s (0:03:26)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 207.4 + }, + { + "mutation": "the background-policy check is dropped", + "replacements": [ + { + "old": " \"background\": background == 0,\n", + "new": " \"background\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host4-background]" + ], + "summary": "3 failed, 239 passed in 206.98s (0:03:26)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 207.6 + }, + { + "mutation": "the QoS check is dropped", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host5-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host6-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host7-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host8-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host9-qos]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "9 failed, 233 passed in 130.32s (0:02:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 130.8 + }, + { + "mutation": "the load check is dropped", + "replacements": [ + { + "old": " \"load\": load < cpus,\n", + "new": " \"load\": True,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host10-load]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host11-load]" + ], + "summary": "4 failed, 238 passed in 163.42s (0:02:43)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 164.0 + }, + { + "mutation": "the QoS check passes a background clamp", + "replacements": [ + { + "old": "QOS_CLASS_DEFAULT = 0x15\n", + "new": "QOS_CLASS_DEFAULT = 0x09\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host5-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host6-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host8-qos]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "7 failed, 235 passed in 144.97s (0:02:24)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 145.4 + }, + { + "mutation": "the QoS check passes an unknown class below the default", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": qos not in (0x11, 0x09, 0x05),\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host8-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host9-qos]" + ], + "summary": "2 failed, 240 passed in 130.73s (0:02:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 131.2 + }, + { + "mutation": "the QoS check fails an unspecified class", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": qos >= QOS_CLASS_DEFAULT,\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_unclamped_qos_classes_meet_the_run_conditions[0]" + ], + "summary": "1 failed, 241 passed in 315.18s (0:05:15)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 315.6 + }, + { + "mutation": "a lapsed sleep assertion is not taken again", + "replacements": [ + { + "old": " self.assertions[kind] = self.host.hold(kind)\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_an_assertion_at_the_off_level_is_given_back_and_taken_again" + ], + "summary": "2 failed, 240 passed in 200.81s (0:03:20)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 201.3 + }, + { + "mutation": "the sleep assertions are taken only after the attempt record", + "replacements": [ + { + "old": " conditions = _start_conditions()\n _start_attempt(local, repository_head, script_sha256)\n", + "new": " _start_attempt(local, repository_head, script_sha256)\n conditions = _start_conditions()\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_main_holds_its_sleep_assertions_from_before_the_attempt_to_the_end", + "tests/test_track_b_b0_2_counts.py::test_main_refuses_without_iokit_before_the_attempt[library]", + "tests/test_track_b_b0_2_counts.py::test_main_refuses_without_iokit_before_the_attempt[symbol]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host0-ac_power]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host1-ac_power]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host10-load]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host11-load]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host2-sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host3-nice]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host4-background]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host5-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host6-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host7-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host8-qos]", + "tests/test_track_b_b0_2_counts.py::test_runtime_refuses_to_start_unless_every_run_condition_holds[host9-qos]" + ], + "summary": "15 failed, 227 passed in 218.93s (0:03:38)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 219.4 + }, + { + "mutation": "the run conditions are checked only at the start", + "replacements": [ + { + "old": " self.before_program()\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]", + "tests/test_track_b_b0_2_counts.py::test_the_run_conditions_are_checked_before_every_program" + ], + "summary": "10 failed, 232 passed in 275.96s (0:04:35)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 276.4 + }, + { + "mutation": "a failing condition stops the run at once", + "replacements": [ + { + "old": " if rechecks == RECHECKS:\n", + "new": " if rechecks == 0:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "9 failed, 233 passed in 204.88s (0:03:24)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 205.4 + }, + { + "mutation": "the wait stops one recheck early", + "replacements": [ + { + "old": " if rechecks == RECHECKS:\n", + "new": " if rechecks == RECHECKS - 1:\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[clears]", + "tests/test_track_b_b0_2_counts.py::test_the_bounded_wait_runs_with_a_shortened_bound_and_real_waits[fails]" + ], + "summary": "8 failed, 234 passed in 86.16s (0:01:26)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 86.6 + }, + { + "mutation": "rechecks do not wait", + "replacements": [ + { + "old": " time.sleep(RECHECK_SECONDS)\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[ac_power]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[background]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[load]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[nice]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[qos]", + "tests/test_track_b_b0_2_counts.py::test_a_condition_failing_for_24_hours_stops_the_run_with_its_name[sleep_assertions]", + "tests/test_track_b_b0_2_counts.py::test_a_failed_condition_is_waited_out_and_changes_no_decision" + ], + "summary": "7 failed, 235 passed in 207.87s (0:03:27)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 208.3 + }, + { + "mutation": "an unreached target keeps an earlier round's result", + "replacements": [ + { + "old": " outcome[\"round\"] == ending\n", + "new": " True\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_target_the_ending_round_did_not_reach_is_unreached[wide-2]" + ], + "summary": "1 failed, 241 passed in 96.64s (0:01:36)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 97.2 + }, + { + "mutation": "a class names no sets", + "replacements": [ + { + "old": " for key in (\"sets\", \"superset\", \"subset\")\n", + "new": " for key in (\"superset\", \"subset\")\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_small_b0_1_cell_that_is_a_b0_2_set_withholds", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_holds_this_releases_counts_and_decisions_only" + ], + "summary": "2 failed, 240 passed in 126.92s (0:02:06)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 127.4 + }, + { + "mutation": "a class's sets include B0.1's", + "replacements": [ + { + "old": " list(path) for path in registry.sets if path_rows[path] in held\n", + "new": " list(path) for path in path_rows if path_rows[path] in held\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[coinciding]", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[household]", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[release_panel]", + "tests/test_track_b_b0_2_counts.py::test_each_class_names_this_releases_sets_that_hold_it[worked_rosters]", + "tests/test_track_b_b0_2_counts.py::test_this_releases_targets_do_not_depend_on_earlier_sets" + ], + "summary": "5 failed, 237 passed in 121.02s (0:02:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 121.5 + }, + { + "mutation": "the identifier check is dropped", + "replacements": [ + { + "old": " _require_unit_identifiers(person_wave, anchor, design)\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[anchor-a present anchor row has no person or interview number-row has no person or interview number]", + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[design cluster-a present person has no design stratum or cluster-a present person has no design stratum or cluster]", + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[design row-a present person has no design stratum or cluster-a present person has no design stratum or cluster]", + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[design stratum-a present person has no design stratum or cluster-a present person has no design stratum or cluster]", + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[person id-a present person-wave row has no person or interview number-row has no person or interview number]", + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[person-wave-a present person-wave row has no person or interview number-row has no person or interview number]" + ], + "summary": "6 failed, 236 passed in 208.09s (0:03:28)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 208.8 + }, + { + "mutation": "the identifier check counts every row", + "replacements": [ + { + "old": " rows = {\"person-wave\": _present(person_wave), \"anchor\": _present(anchor)}\n", + "new": " rows = {\"person-wave\": person_wave, \"anchor\": anchor}\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_missing_identifier_on_a_unit_no_set_counts_is_no_stop" + ], + "summary": "1 failed, 241 passed in 251.01s (0:04:11)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 251.7 + }, + { + "mutation": "the audit saves the atom sizes to a file in the temporary directory (np.save)", + "replacements": [ + { + "old": " truth = model[\"sizes\"]\n", + "new": " truth = model[\"sizes\"]\n np.save(os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), \"b02_atoms.npy\"), truth)\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_every_output_the_script_can_make_names_no_value", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages0-before0-4-3-withheld]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages1-before1-0-7-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_relabeling_deaths_and_moves_changes_no_written_byte" + ], + "summary": "5 failed, 237 passed in 328.89s (0:05:28)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 329.6 + }, + { + "mutation": "the local copy marks a result settled after round 1 with a trailing dot", + "replacements": [ + { + "old": " {key: target[key] for key in TARGET_FIELDS if key in target}\n", + "new": " {**{key: target[key] for key in TARGET_FIELDS if key in target}, **({\"result\": target[\"result\"] + \".\"} if target.get(\"round\", 1) > 1 else {})}\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[falling]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[late]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[rising]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_is_the_same_whatever_the_rounds" + ], + "summary": "4 failed, 238 passed in 206.35s (0:03:26)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 207.0 + }, + { + "mutation": "the local copy orders this release's targets by settling round", + "replacements": [ + { + "old": " for target in audit[\"targets\"]\n if not target[\"earlier\"]\n", + "new": " for target in sorted(audit[\"targets\"], key=lambda t: -t.get(\"round\", 0))\n if not target[\"earlier\"]\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_ignores_rounds_that_order_or_mark_targets[rising]", + "tests/test_track_b_b0_2_counts.py::test_the_local_copy_is_the_same_whatever_the_rounds" + ], + "summary": "2 failed, 240 passed in 140.61s (0:02:20)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 141.3 + }, + { + "mutation": "B0.1's household sets are not registered", + "replacements": [ + { + "old": " for path, (unit_type, units, terms) in sets.items():\n value = sum(", + "new": " for path, (unit_type, units, terms) in sets.items():\n if unit_type != \"persons\":\n continue\n value = sum(", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households" + ], + "summary": "1 failed, 241 passed in 153.82s (0:02:33)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 154.5 + }, + { + "mutation": "an assertion at the off level counts as held", + "replacements": [ + { + "old": " and level.value == _ASSERTION_ON\n", + "new": " or True\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_assertion_set_to_the_off_level_reads_as_not_held", + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[False-True-0]", + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[True-True-0]" + ], + "summary": "3 failed, 239 passed in 117.76s (0:01:57)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 118.3 + }, + { + "mutation": "an assertion at the off level is not given back before it is taken again", + "replacements": [ + { + "old": " # One that exists at the off level is given back first.\n self.host.release(assertion)\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_assertion_at_the_off_level_is_given_back_and_taken_again" + ], + "summary": "1 failed, 241 passed in 164.41s (0:02:44)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 165.0 + }, + { + "mutation": "the assertion check does not give back the properties it copies", + "replacements": [ + { + "old": " self.cf.CFRelease(properties)\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[False-True-0]", + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[False-True-255]", + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[True-True-0]", + "tests/test_track_b_b0_2_counts.py::test_the_host_calls_give_back_every_object_they_own[True-True-255]" + ], + "summary": "4 failed, 238 passed in 121.72s (0:02:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 122.3 + }, + { + "mutation": "the identifier check does not require a design stratum", + "replacements": [ + { + "old": " complete = design.dropna(subset=[\"person_id\", \"stratum\", \"cluster\"])\n", + "new": " complete = design.dropna(subset=[\"person_id\", \"cluster\"])\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_present_unit_without_an_identifier_stops_the_run_while_reading[design stratum-a present person has no design stratum or cluster-a present person has no design stratum or cluster]" + ], + "summary": "1 failed, 241 passed in 126.24s (0:02:06)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 126.8 + }, + { + "mutation": "the exemption is never applied", + "replacements": [ + { + "old": " outcome = {**outcome, \"result\": \"exempt\"}\n", + "new": " pass\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table0]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "10 failed, 232 passed in 139.85s (0:02:19)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 140.5 + }, + { + "mutation": "the exemption compares against the round's own programs", + "replacements": [ + { + "old": " if outcome[\"result\"] == \"narrow\" and exempt(\n earlier, targets[index], outcome, seconds=seconds\n ):", + "new": " if outcome[\"result\"] == \"narrow\" and exempt(\n earlier and (system, earlier[1]),\n targets[index],\n outcome,\n seconds=seconds,\n ):", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households" + ], + "summary": "5 failed, 237 passed in 115.32s (0:01:55)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 116.0 + }, + { + "mutation": "only a target of true size 1 to 9 can be exempt", + "replacements": [ + { + "old": " if earlier is None or outcome.get(\"proof\") != \"optima\":\n return False\n", + "new": " if earlier is None or outcome.get(\"proof\") != \"optima\":\n return False\n if target[\"true_size\"] >= DISCLOSURE_THRESHOLD:\n return False\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "9 failed, 233 passed in 154.27s (0:02:34)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 154.9 + }, + { + "mutation": "a target holding an atom no B0.1 set counts can be exempt", + "replacements": [ + { + "old": " if uncounted[columns].any():\n return False\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed" + ], + "summary": "1 failed, 241 passed in 123.87s (0:02:03)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 124.5 + }, + { + "mutation": "an unproven exemption program counts as exempt", + "replacements": [ + { + "old": " return False\n (maximum, _), (minimum, _) = extremes\n", + "new": " return True\n (maximum, _), (minimum, _) = extremes\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed" + ], + "summary": "1 failed, 241 passed in 122.98s (0:02:02)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 123.6 + }, + { + "mutation": "a range within B0.1's, not equal to it, is exempt", + "replacements": [ + { + "old": " return [minimum, maximum] == outcome[\"range\"]\n", + "new": " low, high = outcome[\"range\"]\n return minimum <= low and high <= maximum\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_earlier_exact_count_narrows_a_hidden_band", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_b0_1s_exact_household_count_withholds_a_small_bands_households", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed" + ], + "summary": "6 failed, 236 passed in 115.17s (0:01:55)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 115.8 + }, + { + "mutation": "the exemption programs both maximize", + "replacements": [ + { + "old": " for sense in (1, -1)\n", + "new": " for sense in (1, 1)\n", + "occurrence": 1, + "anchor_occurrences": 2 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "10 failed, 232 passed in 121.90s (0:02:01)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 122.4 + }, + { + "mutation": "the exemption reads its least and greatest sizes swapped", + "replacements": [ + { + "old": " (maximum, _), (minimum, _) = extremes\n return", + "new": " (minimum, _), (maximum, _) = extremes\n return", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_programs_depend_only_on_the_publications[table1]" + ], + "summary": "10 failed, 232 passed in 184.16s (0:03:04)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 184.6 + }, + { + "mutation": "the exemption programs are not recorded", + "replacements": [ + { + "old": " outcome[\"programs\"].extend([\"earlier maximum\", \"earlier minimum\"])\n", + "new": "", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]", + "tests/test_track_b_b0_2_counts.py::test_the_exemption_fails_closed" + ], + "summary": "4 failed, 238 passed in 172.23s (0:02:52)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 172.8 + }, + { + "mutation": "an exempt target is disclosed", + "replacements": [ + { + "old": " settled[index] = {**outcome, \"round\": len(rounds) + 1}\n if outcome[\"result\"] != \"narrow\":\n", + "new": " settled[index] = {**outcome, \"round\": len(rounds) + 1}\n if outcome[\"result\"] == \"exempt\":\n added.append(index)\n continue\n if outcome[\"result\"] != \"narrow\":\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader", + "tests/test_track_b_b0_2_counts.py::test_classes_b0_1_already_fixes_are_exempt_and_release", + "tests/test_track_b_b0_2_counts.py::test_relabeling_a_death_and_a_move_changes_no_written_byte[ages2-before2-0-6-released]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-1]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[atom-2]", + "tests/test_track_b_b0_2_counts.py::test_switching_off_one_exemption_withholds_on_that_target_alone[difference-3]" + ], + "summary": "8 failed, 234 passed in 184.39s (0:03:04)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 184.9 + }, + { + "mutation": "an exempt target is not tested again", + "replacements": [ + { + "old": " if settled.get(index, {}).get(\"result\") == \"irrelevant\":\n", + "new": " if settled.get(index, {}).get(\"result\") in (\n \"irrelevant\",\n \"exempt\",\n ):\n", + "occurrence": 1, + "anchor_occurrences": 1 + } + ], + "exit_code": 1, + "failed_tests": [ + "tests/test_track_b_b0_2_counts.py::test_a_disclosure_that_narrows_an_exempt_target_ends_its_exemption", + "tests/test_track_b_b0_2_counts.py::test_an_exempt_difference_disclosed_instead_ends_a_class_exemption", + "tests/test_track_b_b0_2_counts.py::test_audit_with_earlier_counts_agrees_with_an_exhaustive_reader" + ], + "summary": "3 failed, 239 passed in 130.07s (0:02:10)", + "status": "killed", + "restored": true, + "restored_source_sha256": "647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321", + "elapsed_seconds": 130.5 + } + ] +} diff --git a/docs/design/track_b_b0_2_counts_report.txt b/docs/design/track_b_b0_2_counts_report.txt new file mode 100644 index 00000000..8e3d7df3 --- /dev/null +++ b/docs/design/track_b_b0_2_counts_report.txt @@ -0,0 +1,503 @@ +Track B B0.2 counts: revised protocol version 1, review round 1 + +Changes and review findings + Known ages are integers 1..cohorts.age67._MAX_AGE_CODE. Ages 0, 999, + fractions and other invalid values enter unknown_age, never an age band + or R_TA_PROXY. Roster bands plus unknown_age partition persons; the + youngest band is now 1_24. Tests use the actual named readers on invented + fixed-width products and hand-count ages 81/125 and 2009 lag origins. + Integer-bounds recovery and the unpublished co-resident remainder are + addressed by a complete set registry, membership-signature atoms, and + every sensitive published-set/subset difference. Wave households have + separate types; design clusters include stratum. Identical sets and + objectives share rows/audits. Sparse matrices feed two scipy.optimize.milp + programs per distinct sensitive quantity, with integer nonnegative atom + sizes, 60 seconds per program, zero MIP gap and one HiGHS thread. Both + proven optima and width >=4 are required. No optimum means unprotected. + Effective sizes supply the specified person/household/design-cluster + lower bounds. Intermediate rounding was removed before coarsening. + All counts/effective sizes use halves-up coarsening and the 10/50/100/500 + ladder. If no base passes, only the disclosure block is published. Public + metadata never names a quantity; sets/memberships are never serialized. + Unsuppressed counts, atom sizes and audit results go only to the local + copy. scipy.milp has no initial-solution argument; true atom sizes are + checked as a feasible witness instead. + Frozen status and the block pin are mandatory before input resolution. + An exclusive, fsynced version-specific attempt record holds head, script + SHA-256 and UTC start time before any PSID file can open. Its name is + independent of output filenames; the fixed B0.2 evidence folder prevents + changing the parent directory to bypass the version lock. Existing clean, + thread, path, symlink and exclusive-output protections remain in place. + +Protocol pin (block unchanged; status remains DRAFT) + f2fd0a3eaf7cbfd99da21d7f885ca05e9e6b62764fe4db895b0f28b84ccd4bc6 + Exact bytes from through , + excluding the following newline. No freeze or real-data run occurred. + +Invented realistic-size timing + 30,000 persons; 415 person sets; six effective sizes; + 413 membership atoms; sparse matrix 271 x 413 with 13,298 nonzeros. + Thirteen collection waves, age bands, retention/validity profiles, + accuracy subsets, lag intersections and origin/later support, plus rare + singleton cells to exercise the ladder. This is the measured invented + fixture, not a worst-case bound for all possible membership patterns. + B=10: 22 quantities audited, 21 failed. B=50: 22 audited, none failed. + All 88 integer programs reached proven optima. Construction plus the + complete audit/ladder: 132.475925s (2m12.48s), under 60 minutes; + audit/ladder alone: 132.240612s. MILPs dominated model construction. + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD PYTHONDONTWRITEBYTECODE=1 /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python /tmp/b02-r1-mutations-timing/benchmark.py > /tmp/b02-r1-mutations-timing/benchmark.log 2>&1 + INVENTED REALISTIC-SIZE AUDIT PASS + +Validation (serial; exact commands, with final lines) + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python -m pytest -p no:xdist -p no:cacheprovider -q tests/test_track_b_b0_2_counts.py tests/test_track_b_b0_1_counts.py > /tmp/b02-r1-tests.log 2>&1 + 95 passed in 885.84s (0:14:45) + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python -m pytest -p no:xdist -p no:cacheprovider -q tests/test_track_b_b0_1_counts.py > /tmp/b02-r1-b01.log 2>&1 + 22 passed in 142.04s (0:02:22) + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python -m black --version + python -m black, 26.5.1 (compiled: yes) + Python (CPython) 3.14.7 + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python -m black --check -l 79 scripts/track_b_b0_2_counts.py tests/test_track_b_b0_2_counts.py > /tmp/b02-r1-black.log 2>&1 + 2 files would be left unchanged. + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python -m ruff check scripts/track_b_b0_2_counts.py tests/test_track_b_b0_2_counts.py > /tmp/b02-r1-ruff.log 2>&1 + All checks passed! + +Mutations + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE PYTHONPATH=$PWD/src:$PWD PYTHONDONTWRITEBYTECODE=1 /Users/maxghenis/PolicyEngine/microcosm-dynamics/.venv/bin/python /tmp/b02-r1-mutations-timing/run_fork_mutations.py /private/tmp/b02-r1-mutations-timing/specs.json > /tmp/b02-r1-mutations-timing/fork-mutations.log 2>&1 + 20/20 mutations killed + FORK MUTATION REPORT RECORDED; SOURCE RESTORED + Six original structural mutations remain; threshold, raw-effective-size + and path mutations were adapted. The old subtraction-complement check + is replaced by the published-subset-difference audit mutation. + Serial fresh child processes reuse parent-cached dependency imports; + the parent never runs an optimizer. Every child evicts and reloads the + script and test module before its targeted pytest.main invocation. + Four pytest import-rewrite warnings per child concern cached Hypothesis + and AnyIO plugins, not the reloaded script or tests. + Exact pytest invocations and equivalent reproduction commands, old/new + source text, exit codes, failed nodes, final lines and restoration + digests are recorded in + docs/design/track_b_b0_2_counts_mutations.json. + 1. band edges: 1 failed, 4 warnings in 16.28s (killed). + 2. lag set: 1 failed, 4 warnings in 14.20s (killed). + 3. R_TA_PROXY age threshold: 1 failed, 4 warnings in 4.23s (killed). + 4. outside-B2 household exclusion: 1 failed, 4 warnings in 6.66s (killed). + 5. valid-row rule: 1 failed, 4 warnings in 6.47s (killed). + 6. B3L origin wave: 1 failed, 4 warnings in 16.08s (killed). + 7. sensitive-quantity threshold changed from 10 to 9: 1 failed, 10 passed, 4 warnings in 16.72s (killed). + 8. effective sizes left uncoarsened: 1 failed, 4 warnings in 30.49s (killed). + 9. local copy allowed inside repository: 2 failed, 4 warnings in 8.21s (killed). + 10. coarsening rounds halves down: 2 failed, 4 warnings in 4.89s (killed). + 11. atom audit skips published subset differences: 1 failed, 4 warnings in 7.31s (killed). + 12. effective-size lower bounds omitted: 1 failed, 4 warnings in 6.15s (killed). + 13. continuous relaxation replaces integer programs: 4 failed, 1 passed, 4 warnings in 6.39s (killed). + 14. protection width reduced to 3: 1 failed, 4 warnings in 2.77s (killed). + 15. disclosure ladder skipped: 1 failed, 4 warnings in 8.36s (killed). + 16. unknown age counted in 80 and over: 1 failed, 4 warnings in 33.10s (killed). + 17. frozen check removed: 2 failed, 4 warnings in 89.67s (0:01:29) (killed). + 18. attempt record not checked, allowing a silent retry: 1 failed, 4 warnings in 30.99s (killed). + 19. 80-and-over band ends at 80: 1 failed, 4 warnings in 27.70s (killed). + 20. 2009 lag origins dropped: 1 failed, 4 warnings in 31.88s (killed). + The silent-retry mutant disables both the early attempt check and + exclusive attempt creation; removing only one leaves the other defense. + Because valid effective sizes cannot exceed their associated published + count, identical monotone coarsening makes their lower bounds redundant + in ordinary valid tables. The omission mutation is tested by broadening + only the count interval in a unit test, isolating the required effective + bound; the true invented table remains valid. The independent household + effective-size attack and complete structural release also pass. + Every mutation restored the production bytes and verified SHA-256: + fd3e2b177e5da09482501dffbadd5c6ab347712dc5efc372fbaa59dedf207322 + +No-data and integrity declaration + No real data file or codebook was opened. All numerical fixtures and + fixed-width reader products were invented. No real-data count, power + calculation, default counts JSON or real evidence-folder attempt was + created. + No package was installed. The protocol, gates.yaml, runs/, + tests/tier_counts.json and existing src/ files were not edited. Only the + requested script, tests, mutation JSON and report changed. Branch + trackb-b0-2-20261021 remains at f5d22c1d21f4ec92dbd6239672ce98e33fc06d4e. + No commit or push was made, following the task's explicit no-commit rule. + +Post-build correction (main session, before review round 2) + count_interval gave a published multiple equal to B the interval + B/2..3B/2-1. Under the coarsening rule, "B" covers B..3B/2-1, because + smaller values are written " through . + Count script SHA-256, unchanged from 88211bf8 through bfd439cf: + d0a96afe92699ed3ca9f7936b45a9154c115ac69e1b567decc70e454040f7553 + +Validation (serial, in a clean clone at bfd439cf) + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE + PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=$PWD/src:$PWD nice -n 10 + .venv/bin/python -m pytest -q -p no:cacheprovider + tests/test_track_b_b0_2_counts.py tests/test_track_b_b0_1_counts.py + tests/test_track_b_b0_2_exclusions.py + tests/test_track_b_b2_outcome_exclusions.py + tests/test_track_b_b0_1_review_root.py + 293 passed in 58.33s + git diff --name-only -z f8ffc7d9 HEAD -- '*.py' | xargs -0 + .venv/bin/python -m black -l 79 --workers 1 --check + 12 files would be left unchanged. + git diff --name-only -z f8ffc7d9 HEAD -- '*.py' | xargs -0 + .venv/bin/ruff check + All checks passed! + Tier counts: tests/tier_counts.json has unit 18,249 against 18,125 at + master f8ffc7d9 (this module collects 120 unit tests); the other tiers + are unchanged. + +Enumeration checks (docs/design/track_b_b0_2_evidence/, invented tables) + oracle.py restates the rule by brute force, without the integer + programs: every atom-size vector with the truth's publication, atoms + allowed to be empty, one unit type and no relation beyond the rows. + python docs/design/track_b_b0_2_evidence/exhaust_closure.py + structures 96 tables 263424 multi 0 + Every three-atom table (two or more rows, distinct nonempty + signatures, each atom 1 to 14): no release needs a second round. The + closure rounds are therefore tested by forcing one + (test_a_narrow_target_outside_one_to_nine_starts_another_round). + python docs/design/track_b_b0_2_evidence/search.py 2 1500 + {'tables': 562, 'released': 531, 'multi_round': 0, 'theorem_fail': 0, + 'width_fail': 0, 'single_unsound': 0} + Three to five atoms, at most 6,000 candidate vectors: on every + released table the reader's posterior equals the final constraint + set, and every sensitive target spans at least 4 over it. None needed + a second round, and a rule that stopped after its first round would + have left no sensitive target narrow on any of them. + python docs/design/track_b_b0_2_evidence/ranges.py 5 3000 + {'released': 1635, 'sensitive': 2665, 'gaps': 0, 'under5': 0} + On these tables every sensitive target also takes every size in its + range, so at least five sizes. The rule does not promise this; it is + an observation, and the protocol states only the range. + The oracle first built every vector in the caps' product before + filtering; during search.py that reached 30 GB, and the run was + stopped. It now decodes vectors in chunks of one million, with an + early limit. Over 1,228 random tables the two versions gave identical + arrays, order included. Each output above also equals the first + version's: exhaust_closure.py and ranges.py from runs of these scripts + before the change, and search.py from an earlier scratch version of + the same search (same seed, trials and draws) before it was tidied for + commit. + +Calibration on an invented panel (calibration.txt) + invented_panel.py draws, from a seeded generator, the shapes the count + script reads (person-waves, family labor validity, the 2011 anchor and + design fields) for 13 biennial waves, with attrition, deaths, + split-offs, missing ages, zero weights, nonsample sequence numbers and + invalid labor amounts. calibrate.py runs the script's registry, model + and audit at B = 10 and times each program. + 800 households: 2,843 targets (939 sensitive), released, every target + wide (2,773 from the pool, 70 by search), audit 298.5 s. + 3,000 households: 3,112 targets (1,446 sensitive), released, every + target wide (3,028 pool, 84 search), audit 699.2 s. + 9,000 households: 3,776 targets (2,244 sensitive), released, every + target wide (3,653 pool, 123 search), audit 705.2 s. + The slowest seed program took 22.3 s and the slowest target, over all + its programs, 19.1 s; each program's limit is 60 s. No round disclosed + a target. The staged PSID's atom and target counts are not known + before the run; the invented panel's are a calibration, not a bound. + +Mutations (docs/design/track_b_b0_2_counts_mutations.json) + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE + PYTHONDONTWRITEBYTECODE=1 nice -n 10 .venv/bin/python + docs/design/track_b_b0_2_evidence/mutations.py RECORD + FINAL: 45/45 mutations killed; restored + d0a96afe92699ed3ca9f7936b45a9154c115ac69e1b567decc70e454040f7553 + A first run at 88211bf8 killed 42 of 45. Three survived, each a gap in + the tests: targets ordered by true size, a timed-out search counted as + proof, and relations registered whether or not guaranteed. bfd439cf + adds a test for each, and each fails on its mutant and passes on the + script: + test_targets_are_classified_in_one_order_whatever_their_sizes + test_a_timed_out_search_proves_nothing + test_a_relation_the_rows_do_not_guarantee_is_not_registered + The record is the full rerun at bfd439cf. + Each check makes one change from mutation_specs.json and runs the + whole count test module in a fresh pytest process with its own + Hypothesis database. 21 checks carry over from the earlier record, one + renamed ("atom audit skips published subset differences" is now "audit + skips published subset differences"); the ladder check is gone with + the ladder. 24 are new, covering the pool and its seeding, target + order, the search windows, the withhold test, the disclosure rounds, + the relaxation margin, timeouts, exact witness checks, the solver gap, + empty atoms, the relations, the public block, coarsening inputs, ages, + the output writes and the attempt record's flush. + Every check failed between 1 and 15 tests; 15 checks failed exactly + one. Each failed-test id in the record matches one of the 120 node ids + pytest collects from the module. + Every check restored the script's bytes and verified its SHA-256: + d0a96afe92699ed3ca9f7936b45a9154c115ac69e1b567decc70e454040f7553 + +No-data and integrity declaration + No real data file or codebook was opened. Every table and panel above + is invented. No real-data count, power calculation, counts JSON or + evidence-folder attempt record was created. gates.yaml, runs/ and + existing src/ files were not edited. + +Final head (main session, 2026-10-27) + Review rounds 3 to 6 changed the block and the script. The protocol's + preamble and PR #584 list each change. Round 5's delta review (Sol, at + 226d844d) approved the block and the script as they then stood. The + ruling of 2026-10-26 then exempted a target that B0.1's published + counts already confine as tightly (6cca06af). The coordinator read it + as covering every audited target, classes and published-set + differences alike, on the same test (1ed6a266). Round 6's delta review + (Sol, at 1ed6a266) approved the block and the script as they stand. + This section records the evidence at the final head. Where the + sections above differ, this one supersedes them: each program's limit + is now 300 s, B0.1's sets enter the audit as exact counts, and a + target they already confine as tightly is exempt. + +Protocol pin (status remains DRAFT) + 6840f5fa41c897407c5896d4c3729ca5a5c0f1d166faa294e65e42309908ad23 + Exact bytes from through . + Count script SHA-256 at 1ed6a266: + 647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321 + It differs from 6cca06af's + (8cee1ecf58569f4034550817c405151c30e671f70611493a9ebea59f850ba08c) + only in the protocol SHA-256 it checks. + +Validation (serial, in a clean clone at 1ed6a266) + Each command ran with OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 + OMP_WAIT_POLICY=PASSIVE PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src:. at + nice 10 on one core. + .venv/bin/python -m pytest -q -rf -p no:cacheprovider --durations=15 + tests/test_track_b_b0_2_counts.py tests/test_track_b_b0_2_exclusions.py + tests/test_track_b_b2_outcome_exclusions.py + tests/test_track_b_b0_1_review_root.py + 393 passed in 194.25s (0:03:14) + .venv/bin/python -m pytest -q -rf -p no:cacheprovider + tests/test_track_b_b0_1_counts.py + 22 passed in 12.26s + git diff --name-only -z 172f690f HEAD -- '*.py' | xargs -0 + .venv/bin/black -l 79 --workers 1 --check + 13 files would be left unchanged. + git diff --name-only -z 172f690f HEAD -- '*.py' | xargs -0 + .venv/bin/ruff check + All checks passed! + Tier counts: tests/tier_counts.json has unit 19,035 against 18,789 at + master 172f690f (+246), total 25,605. The README tier table matches. + The count module collects 242 unit tests. + +Calibration on invented panels (calibration.txt) + Each program's limit is 300 s, and B0.1's sets are registered as + main() registers them, unless stated otherwise. + At e79f41b5, before the exemption: + 800 and 3,000 households withheld, each in the first round on a + person class of true size 1, narrow at [0, 3]. 9,000 households + released: 3,820 wide, 18 irrelevant (audit 2,245.4 s). Without + B0.1's sets the first two release, every target wide. + At 6cca06af, whose audit is the final head's: + 800 households: 3,081 targets (939 sensitive), released: 3,063 wide, + 5 exempt, 13 irrelevant. Audit 473.6 s. + 3,000 households: 3,228 targets (1,446 sensitive), released: 3,215 + wide, 4 exempt, 9 irrelevant. Audit 1,596.3 s. + 9,000 households: 3,838 targets (2,245 sensitive). The first run + withheld on a stratum atom of true size 1 whose programs reached no + proof within 300 s (357.6 s for the target), with 1,304 targets + unreached. The host slept during it: its power log shows the lid + closed at 23:49:16Z and a wake on opening it at 00:06:22Z, and the + job runner's log records the run's end at 00:06:24Z. That the + sleep caused the timeout is an inference; the power log does not + time programs. calibrate.py holds no sleep assertion. + Rerun with the host awake throughout, the panel releases at both + heads with the same decisions as at e79f41b5 (3,820 wide, 18 + irrelevant; 3,623 from the pool, 197 by search): audit 2,562.2 s + at 6cca06af, at a median five-minute load of 105, and 2,276.1 s at + e79f41b5, at a median of 52. + On the runs with the host awake, the slowest seed program took 22.4 s + and the slowest classification of one target, over all its programs, + 71.3 s (3,000 households). calibrate.py times classification only, not + the exemption's programs that follow it for a narrow target. The + post-exemption calibration jobs ran at nice 10 on 18 CPUs with the + one-minute load at 13 to 81 when each started. The run itself starts + only at nice 0 with the five-minute load below 18, holds two sleep + assertions and checks AC power, and the operator keeps the lid open, + which the script does not check. These panels' timings are a + calibration, not a bound on the run's. + +Singleton diagnostic (singleton.py, singleton.txt) + singleton.py classifies a withholding target again under smaller + publications; a Sol review at e79f41b5 found it sound for this + question. Its runs are at e79f41b5, before the exemption, on the two + panels that withheld there. Each classification uses the first + round's constraints only, with a fresh pool, so a wide result does not + show that a whole audit of the smaller publication would release. + Every verdict below is a proof: two verified points for wide, two + proven optima for narrow. On both panels the narrow target is a person + class of true size 1, narrow in round 1 at [0, 3]: width 3, so narrow, + but not identified exactly. + 800 households: it is in 12 of this release's sets and 12 of B0.1's. + Without any B0.1 set it is wide; with only those 12 B0.1 sets, wide; + with every B0.1 set but those 12, narrow at [0, 3]; without any one + of the 12, narrow at [0, 3]. The B0.1 sets that contain it are + neither enough nor needed: B0.1's exact counts on persons outside it, + with this release's own counts, make it narrow. + 3,000 households: it is in 30 of this release's sets and 17 of B0.1's. + Without any B0.1 set it is wide; with only those 17, wide; with + every B0.1 set but those 17, wide. Without any one of the 17 it stays + narrow, except without b0_1/level_cells/pooled/persons or + b0_1/change_pairs/pooled_one_step/persons, where it is wide. Both + groups are needed together, and those two sets each are, given the + rest. + Even the whole publication leaves both targets at [0, 3], so neither + is a case of B0.1 already fixing a count exactly. Which of the B0.1 + sets that do not contain the target matter is not tested: only the + containing sets are removed one at a time. A Sol review of these + conclusions against the output found them accurate. + +Mutations (docs/design/track_b_b0_2_counts_mutations.json) + A first run of 116 specs at e79f41b5 killed 113. Three survived, each + a gap in the tests, not in the script: + 37 "relations dropped from the audit": no test checked that the + audit uses a relation it registers between unit types. + 53 "a modeled set B0.1 never published accepted": in every test, an + earlier check fired first on a set with no published count. + 66 "the local copy sums every target into class sizes": in every + test table, each difference target was also an atom class. + As the coordinator ruled, they are answered with tests only. abbbf793 + and 8a30da57 add + test_the_audit_uses_the_relations_it_registers (two cases) + test_the_class_sizes_are_this_releases_classes_alone + and an aliased set, which only the later check catches, in + test_the_b0_1_record_must_be_the_one_these_inputs_give + Each fails on its mutant and passes on the script. + 6cca06af adds 11 specs (117 to 127) for the exemption. Run alone at + 6cca06af, all 11 were killed; round 6 reran them and the three former + survivors and found all 14 killed. + The record is the full rerun of all 127 specs at 1ed6a266: + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE + PYTHONDONTWRITEBYTECODE=1 nice -n 10 .venv/bin/python + docs/design/track_b_b0_2_evidence/mutations.py RECORD + FINAL: 127/127 mutations killed; restored + 647b52fd7c260a1e46887f8781a92990cae2fa38165f740d65f530703cc96321 + Every check failed between 1 and 47 tests; 30 failed exactly one. Each + failed-test id in the record matches one of the 242 node ids pytest + collects from the module. Every check restored the script's bytes and + verified their SHA-256. The run took 6 h 13 min, one check at a time. + +Enumeration checks + oracle.py, exhaust_closure.py, search.py, ranges.py and their outputs + are unchanged since 8b92691d. As the round 2 section says, they cover + one unit type and no relation beyond the rows, so they do not exercise + B0.1's sets, the exemption or relations between unit types; the count + module's tests do. + +No-data and integrity declaration (final head) + No real data file or codebook was opened. Every table and panel above + is invented. No real-data count, power calculation, counts JSON or + evidence-folder attempt record was created. gates.yaml, runs/ and + existing src/ files were not edited. + +The run (main session, 2026-10-29) + Version 1 ran once, from a clean clone detached at the freeze, + e8a9fac09b600ddfdaa586f962a49a351d9386b7, inside Fleet ops' + reservation for 08:00 to 11:00Z. + A read-only preflight passed before each launch. It checked: + the window; the host (lid open, AC power, five-minute load below + 18, no scheduled restart, free disk, memory pressure); the clone + (head, clean, FROZEN, the block's pin, the script's SHA-256); that + no attempt record, local copy, run log or counts file existed; and + the script's six run conditions, checked by taking its sleep + assertions and giving them back. + The first launch, at 09:36:00Z, refused to start. The launcher, a zsh + script, started the count as a background job, and zsh runs those at + nice 5 unless its BG_NICE option is off. The script's nice check + failed in _start_conditions(), before the attempt record and before + any read, and its one line was + B0.2 refuses to start: run condition nice fails + A refusal there spends no version. The main session set that log + aside, having read only its last line, and the launcher now turns + BG_NICE off. + The second launch, at 09:38:32Z, started at nice 0, passed every start + check, wrote the attempt record and ran to the end. Its one line of + output was the published file's path. The status is released. + By the main session's account, the host stayed on AC power with its + lid open throughout, as a watch that sampled it each minute recorded, + and the system power log records no sleep or wake while it ran. + This PR commits neither log: as the protocol's stated limit says, the + run's total time can depend on dispositions, and both logs show it. + No time from the run's end is written here. + The main session read only the run log's last line and the published + file. It did not open the local copy or the attempt record. + +Committed release (main session, 2026-10-29) + docs/design/track_b_b0_2_counts.json is the run's published file, + byte for byte (SHA-256 + c8415139befefc6c636e792571defe9fc021b9e96cd1438b09a12d7d7e2480eb). + Its counts are 0, "<10" or a multiple of 10, and its disclosure block + holds the base, the two rules and the status alone. The one other + number among the counts is b3l.origin_wave, 2007, which is geometry. + Four tests in tests/test_track_b_b0_2_counts.py now hold the committed + file to the frozen run: + test_the_committed_release_carries_the_frozen_runs_provenance + test_the_committed_release_discloses_only_base_rules_and_status + test_every_committed_count_is_a_coarsened_value + test_the_committed_counts_have_the_scripts_shape + The last compares every leaf's path with the counts the script builds + from an invented panel. The main session wrote sixteen edited versions + of the file in its place, one at a time: a count of 7, an unrounded + count, "<5", a fraction, true for a count, a withheld status, an added + disclosure key, an added top-level key, a dropped or added count, a + wrong head, script or B0.1 hash, an extra opened file, another origin + wave and another lag. Each failed at least one of the four. It then + restored the file's bytes and checked their SHA-256. + tests/test_track_b_b0_2_exclusions.py lists the counts file as + committed, with only the exposure inventory still to come. The unit + tier grows by these four tests, to 19,039 (total 25,609). + The protocol's status line now records the run. This commit leaves + the block, its pin and the script unchanged. + +Review round 10 answer (main session, 2026-10-29) + Round 10 (Sol) approved e72aec18 and found three nonblocking gaps in + the four committed-release tests: they never checked the allowlist's + content, so a timing field there passed; the shape test recorded + leaves only, so an empty object passed; and another admissible value + in place of a count passed, since nothing tied the file to the run. + This commit closes all three in tests/test_track_b_b0_2_counts.py: + test_the_committed_file_is_the_runs_published_file pins the file's + SHA-256, c8415139..., and requires its bytes to be the script's + own serialization (sorted keys, two-space indent, final newline); + test_the_committed_allowlist_is_the_requested_labels_alone checks + the allowlist's keys, its identifiers, concept patterns, anchor + layout and design variables against the script, each wave's + roles, that every other leaf is a PSID variable name and its + label, and that B0.1's committed record names the same variables + in the waves it covers; + _skeleton now records containers as well as leaves. + The main session wrote eleven edited versions of the file in its + place, one at a time: the file unchanged, a count of 7, another + admissible value, an empty object, a timing field in the allowlist + and one inside an entry, a number for a variable, an empty accuracy + list, a dropped wave, a withheld status and a wrong head. The + unchanged file passed; each of the other ten failed at least one of + the six tests. It then restored the file's bytes and checked their + SHA-256. + The unit tier grows by these two tests, to 19,041 (total 25,611). diff --git a/docs/design/track_b_b0_2_evidence/calibrate.py b/docs/design/track_b_b0_2_evidence/calibrate.py new file mode 100644 index 00000000..dd2ca70d --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/calibrate.py @@ -0,0 +1,125 @@ +"""Time B0.2's disclosure audit on an invented panel. + +INVENTED DATA ONLY. ``invented_panel.py`` builds the panel from a seeded +generator; no PSID file is read. The script builds the count script's +registry and model, runs its audit at the fixed base, and prints the +model's size, the audit's status and results, and its slowest targets. +As ``main()`` does, it registers B0.1's sets, rebuilt from the same panel, +at the counts B0.1's own script gives; ``--without-b0-1`` leaves them out, +as the runs before review round 3's answer did. +It wraps ``classify`` and ``AuditSystem.direction`` only to time them; the +audit itself is unchanged. + +Run from the repository root, with the thread settings the protocol uses:: + + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE \\ + PYTHONPATH=src:. python docs/design/track_b_b0_2_evidence/calibrate.py \\ + HOUSEHOLDS SEED [--without-b0-1] + +The output for the report is committed beside it as ``calibration.txt``. +""" + +from __future__ import annotations + +import sys +import time +from collections import Counter +from pathlib import Path + +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE.parents[2] / "scripts")) +sys.path.insert(0, str(HERE)) + +import track_b_b0_2_counts as script # noqa: E402 +from invented_panel import invented_inputs # noqa: E402 + + +def main(households: int, seed: int, with_b0_1: bool = True) -> None: + started = time.monotonic() + people, labor, anchor, design = invented_inputs(households, seed) + validity = { + wave: script.validity_frame(frame) for wave, frame in labor.items() + } + counts = script.structural_counts(people, validity, anchor, design) + registry = script.disclosure_registry(people, validity, anchor, design) + earlier = {} + if with_b0_1: + earlier = script.b0_1_sets(people, validity) + script.register_b0_1( + registry, earlier, script.b0_1_structural_counts(people, validity) + ) + model = script.build_model(registry) + sensitive = sum(1 <= t["true_size"] < 10 for t in model["targets"]) + print( + f"households={households} seed={seed} " + f"persons/wave~{len(people) // 13} sets={len(registry.sets)} " + f"b0_1_sets={len(earlier)} " + f"rows={model['matrix'].shape[0]} " + f"atoms={dict(Counter(model['unit_types']))} " + f"targets={len(model['targets'])} sensitive={sensitive} " + f"build={time.monotonic() - started:.1f}s", + flush=True, + ) + slow = [] + classify = script.classify + direction = script.AuditSystem.direction + + def timed_classify(system, target, pool, *, seconds): + start = time.monotonic() + outcome = classify(system, target, pool, seconds=seconds) + elapsed = time.monotonic() - start + record = outcome[0] + slow.append( + ( + round(elapsed, 1), + target["kind"], + target["true_size"], + record["result"], + record.get("proof"), + ) + ) + return outcome + + def timed_direction(self, weights, *, seconds): + start = time.monotonic() + point = direction(self, weights, seconds=seconds) + print( + f" seed program {time.monotonic() - start:.1f}s " + f"point={point is not None}", + flush=True, + ) + return point + + script.classify = timed_classify + script.AuditSystem.direction = timed_direction + start = time.monotonic() + audit = script.audit_counts(counts, registry, script.DISCLOSURE_BASE) + results = Counter(t["result"] for t in audit["targets"]) + proofs = Counter(t.get("proof") for t in audit["targets"]) + narrow_sensitive = sum( + t["result"] == "narrow" and 1 <= t["true_size"] < 10 + for t in audit["targets"] + ) + print( + f"B={audit['base']} status={audit['status']} " + f"rounds={[len(r['disclosed']) for r in audit['rounds']]} " + f"results={dict(results)} proofs={dict(proofs)} " + f"pool={audit['pool']} narrow_sensitive={narrow_sensitive} " + f"audit={time.monotonic() - start:.1f}s", + flush=True, + ) + print(" slowest targets:", sorted(slow, reverse=True)[:5], flush=True) + withholding = [ + (t["kind"], t["unit_type"], t["true_size"], t["earlier"]) + for t in audit["targets"] + if t["result"] == "narrow" and 1 <= t["true_size"] < 10 + ] + print(" narrow sensitive (kind, unit, size, earlier):", withholding) + + +if __name__ == "__main__": + main( + int(sys.argv[1]), + int(sys.argv[2]), + with_b0_1="--without-b0-1" not in sys.argv[3:], + ) diff --git a/docs/design/track_b_b0_2_evidence/calibration.txt b/docs/design/track_b_b0_2_evidence/calibration.txt new file mode 100644 index 00000000..d3aa8c30 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/calibration.txt @@ -0,0 +1,207 @@ +B0.2 disclosure audit on invented panels (INVENTED DATA ONLY; no PSID file read). +Script: docs/design/track_b_b0_2_evidence/calibrate.py (SHA-256 c714f0d8...), +loading scripts/track_b_b0_2_counts.py (7ec6ee28...) and invented_panel.py +(38c17076...), each with the bytes committed at e79f41b5. Of the three, only the +count script has changed since; the runs at 6cca06af, at the end, use its new bytes. +Run 2026-10-26 from a clean clone at e79f41b5, one job at a time on a shared host, +each job at nice 10 on one core, with OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 +OMP_WAIT_POLICY=PASSIVE PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src:. The one-minute +load average was 63 to 97 when each job started. Times are elapsed seconds on +Python's time.monotonic() clock and vary with host load. Each program's limit +is 300 s (PROGRAM_SECONDS). + +The first three runs register B0.1's sets at the counts B0.1's own script gives, +as main() does (b0_1_sets=71). The last two leave them out (--without-b0-1), on +the same two panels that withhold with them. + +An earlier calibration, at 88211bf8 on 2026-10-24, used a 60 s limit and no B0.1 +sets. On the same three panels it released, every target wide: 800 households, +2,843 targets (audit 298.5 s); 3,000 households, 3,112 (699.2 s); 9,000 +households, 3,776 (705.2 s). + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 800 1 +households=800 seed=1 persons/wave~1341 sets=588 b0_1_sets=71 rows=512 atoms={'clusters': 156, 'households_1999': 5, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 61, 'households_2009': 5, 'households_2011': 119, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 5, 'households_2021': 6, 'households_2023': 6, 'persons': 685, 'strata': 80} targets=3081 sensitive=939 build=0.5s + seed program 7.5s point=True + seed program 3.5s point=True + seed program 5.8s point=True + seed program 3.2s point=True + seed program 8.5s point=True + seed program 8.3s point=True + seed program 4.0s point=True + seed program 3.7s point=True + seed program 3.2s point=True +B=10 status=withheld rounds=[0] results={'wide': 366, 'narrow': 1, 'unreached': 2714} proofs={'pool': 327, 'search': 39, 'optima': 1, None: 2714} pool=48 narrow_sensitive=1 audit=246.8s + slowest targets: [(7.7, 'atom', 3, 'wide', 'search'), (7.4, 'atom', 1, 'wide', 'search'), (7.4, 'atom', 1, 'wide', 'search'), (7.3, 'atom', 1, 'wide', 'search'), (7.1, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [('atom', 'persons', 1, False)] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 3000 2 +households=3000 seed=2 persons/wave~5108 sets=588 b0_1_sets=71 rows=506 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 88, 'households_2009': 6, 'households_2011': 197, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 1747, 'strata': 80} targets=3228 sensitive=1446 build=1.1s + seed program 16.5s point=True + seed program 12.7s point=True + seed program 5.6s point=True + seed program 15.1s point=True + seed program 18.8s point=True + seed program 6.6s point=True + seed program 3.6s point=True + seed program 11.3s point=True + seed program 18.4s point=True +B=10 status=withheld rounds=[0] results={'wide': 1251, 'narrow': 1, 'unreached': 1976} proofs={'search': 81, 'pool': 1170, 'optima': 1, None: 1976} pool=90 narrow_sensitive=1 audit=1012.2s + slowest targets: [(25.3, 'atom', 1, 'wide', 'search'), (22.4, 'atom', 3, 'wide', 'search'), (21.8, 'atom', 1, 'wide', 'search'), (20.3, 'atom', 1, 'wide', 'search'), (20.0, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [('atom', 'persons', 1, False)] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 9000 3 +households=9000 seed=3 persons/wave~15348 sets=588 b0_1_sets=71 rows=482 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 123, 'households_2009': 6, 'households_2011': 272, 'households_2013': 11, 'households_2015': 11, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 3795, 'strata': 77} targets=3838 sensitive=2245 build=2.3s + seed program 7.3s point=True + seed program 6.7s point=True + seed program 8.7s point=True + seed program 10.0s point=True + seed program 20.4s point=True + seed program 6.9s point=True + seed program 9.5s point=True + seed program 10.5s point=True + seed program 14.1s point=True +B=10 status=released rounds=[0] results={'wide': 3820, 'irrelevant': 18} proofs={'pool': 3623, 'search': 197, None: 18} pool=206 narrow_sensitive=0 audit=2245.4s + slowest targets: [(31.5, 'atom', 1, 'wide', 'search'), (31.4, 'atom', 1, 'wide', 'search'), (31.0, 'atom', 3, 'wide', 'search'), (30.0, 'atom', 7, 'wide', 'search'), (29.7, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 800 1 --without-b0-1 +households=800 seed=1 persons/wave~1341 sets=588 b0_1_sets=0 rows=462 atoms={'clusters': 156, 'households_1999': 5, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 61, 'households_2009': 5, 'households_2011': 72, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 5, 'households_2021': 6, 'households_2023': 6, 'persons': 471, 'strata': 80} targets=2843 sensitive=939 build=0.4s + seed program 0.8s point=True + seed program 2.8s point=True + seed program 2.5s point=True + seed program 3.0s point=True + seed program 2.9s point=True + seed program 2.6s point=True + seed program 3.8s point=True + seed program 2.5s point=True + seed program 3.0s point=True +B=10 status=released rounds=[0] results={'wide': 2843} proofs={'pool': 2773, 'search': 70} pool=79 narrow_sensitive=0 audit=275.6s + slowest targets: [(8.4, 'atom', 1, 'wide', 'search'), (7.6, 'atom', 1, 'wide', 'search'), (6.8, 'atom', 1, 'wide', 'search'), (6.7, 'atom', 1, 'wide', 'search'), (6.6, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 3000 2 --without-b0-1 +households=3000 seed=2 persons/wave~5108 sets=588 b0_1_sets=0 rows=456 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 88, 'households_2009': 6, 'households_2011': 115, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 1050, 'strata': 80} targets=3112 sensitive=1446 build=0.6s + seed program 1.9s point=True + seed program 3.4s point=True + seed program 12.3s point=True + seed program 7.3s point=True + seed program 3.2s point=True + seed program 4.0s point=True + seed program 8.5s point=True + seed program 14.3s point=True + seed program 19.8s point=True +B=10 status=released rounds=[0] results={'wide': 3112} proofs={'search': 84, 'pool': 3028} pool=93 narrow_sensitive=0 audit=667.8s + slowest targets: [(19.8, 'atom', 1, 'wide', 'search'), (14.6, 'atom', 1, 'wide', 'search'), (14.4, 'atom', 2, 'wide', 'search'), (13.0, 'atom', 2, 'wide', 'search'), (12.8, 'atom', 2, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] + +At 6cca06af, after the exemption (the coordinator's ruling of 2026-10-26). +calibrate.py and invented_panel.py are the bytes above; the count script is +8cee1ecf... At 1ed6a266 the count script is 647b52fd..., which differs from +6cca06af's only in the protocol SHA-256 it checks, so its audit is the same. +Run 2026-10-26 and 2026-10-27 from clean clones, under the settings above, with +B0.1's sets registered. The one-minute load average was 13 to 81 when each job +started. + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 800 1 +households=800 seed=1 persons/wave~1341 sets=588 b0_1_sets=71 rows=512 atoms={'clusters': 156, 'households_1999': 5, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 61, 'households_2009': 5, 'households_2011': 119, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 5, 'households_2021': 6, 'households_2023': 6, 'persons': 685, 'strata': 80} targets=3081 sensitive=939 build=0.9s + seed program 9.3s point=True + seed program 3.9s point=True + seed program 7.0s point=True + seed program 3.7s point=True + seed program 9.8s point=True + seed program 9.4s point=True + seed program 4.5s point=True + seed program 4.5s point=True + seed program 3.9s point=True +B=10 status=released rounds=[0] results={'wide': 3063, 'exempt': 5, 'irrelevant': 13} proofs={'pool': 2999, 'search': 64, 'optima': 5, None: 13} pool=73 narrow_sensitive=0 audit=473.6s + slowest targets: [(18.6, 'atom', 1, 'narrow', 'optima'), (9.5, 'atom', 3, 'wide', 'search'), (9.3, 'atom', 1, 'wide', 'search'), (9.1, 'atom', 1, 'narrow', 'optima'), (8.9, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 3000 2 +households=3000 seed=2 persons/wave~5108 sets=588 b0_1_sets=71 rows=506 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 88, 'households_2009': 6, 'households_2011': 197, 'households_2013': 10, 'households_2015': 10, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 1747, 'strata': 80} targets=3228 sensitive=1446 build=1.4s + seed program 20.6s point=True + seed program 14.5s point=True + seed program 7.4s point=True + seed program 20.4s point=True + seed program 22.0s point=True + seed program 7.9s point=True + seed program 4.4s point=True + seed program 11.5s point=True + seed program 22.4s point=True +B=10 status=released rounds=[0] results={'wide': 3215, 'exempt': 4, 'irrelevant': 9} proofs={'search': 101, 'pool': 3114, 'optima': 4, None: 9} pool=110 narrow_sensitive=0 audit=1596.3s + slowest targets: [(71.3, 'atom', 2, 'wide', 'search'), (43.8, 'atom', 1, 'narrow', 'optima'), (30.2, 'atom', 1, 'wide', 'search'), (26.7, 'atom', 1, 'wide', 'search'), (26.7, 'atom', 1, 'narrow', 'optima')] + narrow sensitive (kind, unit, size, earlier): [] + +$ python docs/design/track_b_b0_2_evidence/calibrate.py 9000 3 +households=9000 seed=3 persons/wave~15348 sets=588 b0_1_sets=71 rows=482 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 123, 'households_2009': 6, 'households_2011': 272, 'households_2013': 11, 'households_2015': 11, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 3795, 'strata': 77} targets=3838 sensitive=2245 build=3.6s + seed program 10.0s point=True + seed program 8.4s point=True + seed program 10.1s point=True + seed program 12.2s point=True + seed program 23.9s point=True + seed program 8.8s point=True + seed program 12.8s point=True + seed program 14.3s point=True + seed program 18.8s point=True +B=10 status=withheld rounds=[0] results={'wide': 2533, 'narrow': 1, 'unreached': 1304} proofs={'pool': 2338, 'search': 195, 'undetermined': 1, None: 1304} pool=204 narrow_sensitive=1 audit=3270.2s + slowest targets: [(357.6, 'atom', 1, 'narrow', 'undetermined'), (45.0, 'atom', 1, 'wide', 'search'), (41.9, 'atom', 7, 'wide', 'search'), (41.5, 'atom', 1, 'wide', 'search'), (39.3, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [('atom', 'strata', 1, False)] + +The host slept during this run. Its power log (pmset -g log) shows the lid +closed at 23:49:16Z, then maintenance sleeps and dark wakes, and a wake on +opening the lid at 00:06:22Z. The job runner's log, whose times are UTC from +the system clock, records the run's start at 23:00:19Z and its end at +00:06:24Z, 3,965 s apart. Its elapsed field reads 3,277 s. That field comes +from Python's time.monotonic(), as do all of calibrate.py's times, so during +this run that clock advanced about 688 s less than the system clock, and this +run's times, the 357.6 s for the target included, need not equal elapsed time +on the system clock. The run withheld on a target whose programs reached no +proof within the 300 s program limit; a withholding ends the audit at once. +The power log does not time programs, so that the sleep caused this timeout is +an inference, not an observation: the run ended two seconds after the wake, +and the reruns below, with the host awake, release this panel, its slowest +classification taking 44.7 s. calibrate.py holds no sleep assertion. +The count script's main() holds two and checks AC power, and the protocol has +the operator keep the lid open, which the script does not check. + +The same panel was run again at both heads, one after the other, with the host +awake throughout (no sleep in its power log) and the load average sampled every +30 s. Both release with the same decisions as the run at e79f41b5 above. By the +author's account, a first launch of the rerun at 6cca06af, at 00:10:14Z, was +stopped before it printed any output; its runner log records only the start. +It produced no result. The job that ran the two reruns below started at +00:15:51Z. + +At 6cca06af; one-minute load 20.6 to 188.0, median 98.9; +five-minute load 54.4 to 163.7, median 105.4: +$ python docs/design/track_b_b0_2_evidence/calibrate.py 9000 3 +households=9000 seed=3 persons/wave~15348 sets=588 b0_1_sets=71 rows=482 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 123, 'households_2009': 6, 'households_2011': 272, 'households_2013': 11, 'households_2015': 11, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 3795, 'strata': 77} targets=3838 sensitive=2245 build=2.8s + seed program 8.0s point=True + seed program 7.2s point=True + seed program 8.2s point=True + seed program 9.0s point=True + seed program 19.7s point=True + seed program 7.3s point=True + seed program 9.8s point=True + seed program 11.8s point=True + seed program 14.3s point=True +B=10 status=released rounds=[0] results={'wide': 3820, 'irrelevant': 18} proofs={'pool': 3623, 'search': 197, None: 18} pool=206 narrow_sensitive=0 audit=2562.2s + slowest targets: [(44.7, 'atom', 7, 'wide', 'search'), (40.0, 'atom', 1, 'wide', 'search'), (39.1, 'atom', 1, 'wide', 'search'), (39.0, 'atom', 1, 'wide', 'search'), (35.4, 'atom', 1, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] + +At e79f41b5; one-minute load 14.6 to 115.4, median 51.2; +five-minute load 32.8 to 90.7, median 52.3: +$ python docs/design/track_b_b0_2_evidence/calibrate.py 9000 3 +households=9000 seed=3 persons/wave~15348 sets=588 b0_1_sets=71 rows=482 atoms={'clusters': 160, 'households_1999': 6, 'households_2001': 6, 'households_2003': 6, 'households_2005': 6, 'households_2007': 123, 'households_2009': 6, 'households_2011': 272, 'households_2013': 11, 'households_2015': 11, 'households_2017': 6, 'households_2019': 6, 'households_2021': 6, 'households_2023': 6, 'persons': 3795, 'strata': 77} targets=3838 sensitive=2245 build=2.7s + seed program 7.7s point=True + seed program 7.0s point=True + seed program 8.9s point=True + seed program 10.0s point=True + seed program 21.2s point=True + seed program 7.6s point=True + seed program 10.5s point=True + seed program 12.0s point=True + seed program 14.5s point=True +B=10 status=released rounds=[0] results={'wide': 3820, 'irrelevant': 18} proofs={'pool': 3623, 'search': 197, None: 18} pool=206 narrow_sensitive=0 audit=2276.1s + slowest targets: [(34.0, 'atom', 1, 'wide', 'search'), (33.0, 'atom', 7, 'wide', 'search'), (31.8, 'atom', 1, 'wide', 'search'), (30.7, 'atom', 1, 'wide', 'search'), (30.1, 'atom', 3, 'wide', 'search')] + narrow sensitive (kind, unit, size, earlier): [] diff --git a/docs/design/track_b_b0_2_evidence/exhaust_closure.py b/docs/design/track_b_b0_2_evidence/exhaust_closure.py new file mode 100644 index 00000000..88ae5f21 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/exhaust_closure.py @@ -0,0 +1,62 @@ +"""Search every three-atom table for a release that needs a second round. + +INVENTED TABLES ONLY. A table here is any set of two or more published +rows over three atoms whose signatures (the rows that hold each atom) are +distinct and nonempty, and every size vector with each atom in 1 to 14. +For each, ``oracle.release`` applies the rule, and the script prints each +table whose release discloses a target in a closure round. It found none, +which is why the closure rounds are tested by a forced-round unit test +instead (``test_a_narrow_target_outside_one_to_nine_starts_another_round``). + +Run from the repository root:: + + python docs/design/track_b_b0_2_evidence/exhaust_closure.py + +The output is committed beside it as ``exhaust_closure.txt``. +""" + +from __future__ import annotations + +import sys +from itertools import combinations, product +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import oracle # noqa: E402 + +ATOMS = 3 + + +def main() -> None: + rows_available = [ + row + for size in range(1, ATOMS + 1) + for row in combinations(range(ATOMS), size) + ] + structures = 0 + tables = 0 + multi = 0 + for count in range(2, len(rows_available) + 1): + for rows in combinations(rows_available, count): + signatures = { + frozenset(j for j, row in enumerate(rows) if a in row) + for a in range(ATOMS) + } + if len(signatures) != ATOMS or frozenset() in signatures: + continue + structures += 1 + target_list = oracle.targets(rows, ATOMS) + for sizes in product(range(1, 15), repeat=ATOMS): + tables += 1 + status, disclosed, _ = oracle.release( + list(rows), list(sizes), None, target_list + ) + if disclosed: + multi += 1 + print("MULTI", rows, sizes, status, disclosed) + print(f"structures {structures} tables {tables} multi {multi}") + + +if __name__ == "__main__": + main() diff --git a/docs/design/track_b_b0_2_evidence/exhaust_closure.txt b/docs/design/track_b_b0_2_evidence/exhaust_closure.txt new file mode 100644 index 00000000..dc0f2110 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/exhaust_closure.txt @@ -0,0 +1 @@ +structures 96 tables 263424 multi 0 diff --git a/docs/design/track_b_b0_2_evidence/invented_panel.py b/docs/design/track_b_b0_2_evidence/invented_panel.py new file mode 100644 index 00000000..0dce2ef0 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/invented_panel.py @@ -0,0 +1,136 @@ +"""An invented PSID-shaped panel for B0.2's disclosure calibration. + +INVENTED DATA ONLY. Every value is drawn from a seeded generator, and no +PSID file, codebook or survey record is read. The panel has the shapes +B0.2's count script consumes (person-waves, family labor validity, the +2011 anchor and design fields) for 13 biennial waves, with attrition, +deaths, split-offs, missing ages (999), zero weights, nonsample sequence +numbers and invalid labor amounts. ``calibrate.py`` beside it uses it. +""" + +import numpy as np +import pandas as pd + +WAVES = tuple(range(1999, 2024, 2)) +SENTINEL = 9_999_998 + + +def invented_inputs(households=9000, seed=0, strata=80): + rng = np.random.default_rng(seed) + people = [] + labor = {w: [] for w in WAVES} + next_person = 1 + next_household = 1 + live = ( + [] + ) # households: dict(id, members=[(pid, rel, birth)], stratum, cluster) + + def new_household(head_age, wave, spouse=True): + nonlocal next_person, next_household + birth = wave - head_age + members = [(next_person, 10, birth)] + next_person += 1 + if spouse: + members.append((next_person, 20, birth + int(rng.integers(-6, 7)))) + next_person += 1 + for _ in range(int(rng.poisson(0.9 if head_age < 55 else 0.1))): + members.append( + (next_person, 30, birth + int(rng.integers(18, 40))) + ) + next_person += 1 + household = { + "id": next_household, + "members": members, + "stratum": int(rng.integers(1, strata + 1)), + "cluster": int(rng.integers(1, 3)), + } + next_household += 1 + return household + + for _ in range(households): + live.append( + new_household(int(rng.integers(18, 95)), 1999, rng.random() < 0.55) + ) + design = {} + for wave in WAVES: + survivors = [] + for household in live: + if rng.random() < 0.035: # attrition + continue + members = [] + for pid, rel, birth in household["members"]: + age = wave - birth + if rng.random() < ( + 0.002 if age < 60 else 0.02 + (age - 60) * 0.004 + ): + continue + members.append((pid, rel, birth)) + if not members: + continue + if all(rel != 10 for _, rel, _ in members): + pid, _, birth = members[0] + members[0] = (pid, 10, birth) + household["members"] = members + survivors.append(household) + for _ in range(int(households * 0.03)): # split-offs and new entrants + survivors.append( + new_household( + int(rng.integers(18, 35)), wave, rng.random() < 0.4 + ) + ) + live = survivors + for household in live: + interview = wave * 100_000 + household["id"] + for sequence, (pid, rel, birth) in enumerate( + household["members"], 1 + ): + age = wave - birth + if rng.random() < 0.004: + age = 999 + weight = ( + 0.0 + if rng.random() < 0.02 + else float(rng.lognormal(0, 0.6)) + ) + present = sequence if rng.random() > 0.03 else 51 + people.append( + (pid, wave, age, min(present, 51), rel, weight, interview) + ) + design.setdefault( + pid, (household["stratum"], household["cluster"]) + ) + labor[wave].append( + { + "interview": interview, + "head_labor": ( + float(SENTINEL) if rng.random() < 0.03 else 1.0 + ), + "spouse_labor": ( + float(SENTINEL) if rng.random() < 0.05 else 0.0 + ), + "head_acc": int(rng.random() < 0.1), + "spouse_acc": int(rng.random() < 0.08), + } + ) + columns = [ + "person_id", + "period", + "age", + "sequence", + "relationship", + "weight", + "interview", + ] + person_wave = pd.DataFrame(people, columns=columns) + labor = {w: pd.DataFrame(rows) for w, rows in labor.items()} + anchor = ( + person_wave.loc[person_wave["period"] == 2011] + .drop(columns="period") + .copy() + ) + anchor["weight"] = anchor["weight"] * rng.uniform(0.8, 1.2, len(anchor)) + design = pd.DataFrame( + [(pid, s, c) for pid, (s, c) in design.items()], + columns=["person_id", "stratum", "cluster"], + ) + return person_wave, labor, anchor, design diff --git a/docs/design/track_b_b0_2_evidence/mutation_specs.json b/docs/design/track_b_b0_2_evidence/mutation_specs.json new file mode 100644 index 00000000..e2f628fe --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/mutation_specs.json @@ -0,0 +1,1277 @@ +[ + { + "mutation": "band edges", + "replacements": [ + { + "old": "OLDER_BANDS = ((\"62_64\", 62, 64), (\"65_66\", 65, 66), (\"67_69\", 67, 69))", + "new": "OLDER_BANDS = ((\"62_64\", 63, 64), (\"65_66\", 65, 66), (\"67_69\", 67, 69))", + "occurrence": 1 + } + ] + }, + { + "mutation": "lag set", + "replacements": [ + { + "old": "LAGS = (2, 4)", + "new": "LAGS = (2, 6)", + "occurrence": 1 + } + ] + }, + { + "mutation": "R_TA_PROXY age threshold", + "replacements": [ + { + "old": "proxy = proxy[_known_age(proxy[\"age\"]) & (proxy[\"age\"] >= 30)]", + "new": "proxy = proxy[_known_age(proxy[\"age\"]) & (proxy[\"age\"] > 30)]", + "occurrence": 1 + } + ] + }, + { + "mutation": "valid-row rule", + "replacements": [ + { + "old": "rows[\"valid\"], [\"person_id\", \"reference_year\", \"acc_flag\"]", + "new": "rows[\"weight_positive\"], [\"person_id\", \"reference_year\", \"acc_flag\"]", + "occurrence": 1 + } + ] + }, + { + "mutation": "B3L origin wave", + "replacements": [ + { + "old": "B3L_ORIGIN_WAVE = 2007", + "new": "B3L_ORIGIN_WAVE = 2009", + "occurrence": 1 + } + ] + }, + { + "mutation": "sensitive-quantity threshold changed from 10 to 9", + "replacements": [ + { + "old": "DISCLOSURE_THRESHOLD = 10", + "new": "DISCLOSURE_THRESHOLD = 9", + "occurrence": 1 + } + ] + }, + { + "mutation": "effective sizes left uncoarsened", + "replacements": [ + { + "old": "_put(published, path, coarsen(value, base))", + "new": "_put(published, path, value if path[-1].endswith(\"_n_eff\") else coarsen(value, base))", + "occurrence": 1 + } + ] + }, + { + "mutation": "local copy allowed inside repository", + "replacements": [ + { + "old": "raise SystemExit(\"local copy must be outside the repository\")", + "new": "pass # Deliberately permit repository-local copies.", + "occurrence": 1 + } + ] + }, + { + "mutation": "coarsening rounds halves down", + "replacements": [ + { + "old": "return int((Decimal(str(value)) / base + Decimal(\"0.5\")) // 1) * base", + "new": "return int((Decimal(str(value)) / base + Decimal(\"0.5\")).to_integral_value(rounding=\"ROUND_CEILING\") - 1) * base", + "occurrence": 1 + } + ] + }, + { + "mutation": "effective-size lower bounds omitted", + "replacements": [ + { + "old": "for path, count_path in registry.effective_bounds.items():", + "new": "for path, count_path in (): # Deliberately omit effective bounds.", + "occurrence": 1 + } + ] + }, + { + "mutation": "protection width reduced to 3", + "replacements": [ + { + "old": "PROTECTION_WIDTH = 4", + "new": "PROTECTION_WIDTH = 3", + "occurrence": 1 + } + ] + }, + { + "mutation": "unknown age counted in 80 and over", + "replacements": [ + { + "old": "return _known_age(ages) & numeric.between(low, high)", + "new": "return (_known_age(ages) & numeric.between(low, high)) | (~_known_age(ages) & (low == 80))", + "occurrence": 1 + } + ] + }, + { + "mutation": "frozen check removed", + "replacements": [ + { + "old": " _check_protocol()\n", + "new": " pass # Deliberately omit frozen protocol validation.\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "published B interval starts at B/2", + "replacements": [ + { + "old": "return max(int(value - base // 2), base), int(value + base // 2 - 1)", + "new": "return int(value - base // 2), int(value + base // 2 - 1)", + "occurrence": 1 + } + ] + }, + { + "mutation": "80-and-over band ends at 80", + "replacements": [ + { + "old": "(\"80_plus\", 80, age67._MAX_AGE_CODE),", + "new": "(\"80_plus\", 80, 80),", + "occurrence": 1 + } + ] + }, + { + "mutation": "2009 lag origins dropped", + "replacements": [ + { + "old": "if later not in WAVES:", + "new": "if later not in WAVES or wave == 2009:", + "occurrence": 1 + } + ] + }, + { + "mutation": "outside-B2 household exclusion", + "replacements": [ + { + "old": "outside = full[~full[\"interview\"].isin(domain[\"interview\"])]", + "new": "outside = full[~full[\"person_id\"].isin(domain[\"person_id\"])]", + "occurrence": "all" + } + ] + }, + { + "mutation": "attempt record not checked, allowing a silent retry", + "replacements": [ + { + "old": "if attempt.exists() or attempt.is_symlink():", + "new": "if False and (attempt.exists() or attempt.is_symlink()):", + "occurrence": 1 + }, + { + "old": "with attempt.open(\"x\", encoding=\"utf-8\") as stream:", + "new": "with attempt.open(\"w\", encoding=\"utf-8\") as stream:", + "occurrence": 1 + } + ] + }, + { + "mutation": "effective bound for a published B starts at B/2", + "replacements": [ + { + "old": "return max(base, math.ceil(value - base / 2))", + "new": "return max(0, math.ceil(value - base / 2))", + "occurrence": 1 + } + ] + }, + { + "mutation": "audit skips published subset differences", + "replacements": [ + { + "old": "if superset == subset or keys[superset][0] != keys[subset][0]:", + "new": "if True:", + "occurrence": 1 + } + ] + }, + { + "mutation": "continuous relaxation replaces integer programs", + "replacements": [ + { + "old": "integrality=np.full(self.variables, int(integral)),", + "new": "integrality=np.zeros(self.variables),", + "occurrence": 1 + } + ] + }, + { + "mutation": "pool seeded with the true sizes", + "replacements": [ + { + "old": " pool = WitnessPool(len(truth))\n", + "new": " pool = WitnessPool(len(truth))\n pool.add(truth)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "targets taken in order of true size", + "replacements": [ + { + "old": " for index in range(len(targets)):\n if index in disclosed:", + "new": " for index in sorted(range(len(targets)), key=lambda i: targets[i][\"true_size\"]):\n if index in disclosed:", + "occurrence": 1 + } + ] + }, + { + "mutation": "search windows centred on the true size", + "replacements": [ + { + "old": " low, high = pool.values(columns)\n", + "new": " low, high = pool.values(columns)\n low = high = target[\"true_size\"]\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "disclosure test widened to sizes 2-9", + "replacements": [ + { + "old": "if 1 <= targets[index][\"true_size\"] < DISCLOSURE_THRESHOLD:", + "new": "if 2 <= targets[index][\"true_size\"] < DISCLOSURE_THRESHOLD:", + "occurrence": 1 + } + ] + }, + { + "mutation": "an empty pool releases", + "replacements": [ + { + "old": " # No program found a point, so nothing can be shown wide.\n status = \"withheld\"", + "new": " # No program found a point, so nothing can be shown wide.\n pass", + "occurrence": 1 + } + ] + }, + { + "mutation": "a single round, with no reclassification", + "replacements": [ + { + "old": " if not added:\n break\n", + "new": " break\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "disclosed targets left unconstrained", + "replacements": [ + { + "old": "self.disclosed = [tuple(target[\"columns\"]) for target in disclosed]", + "new": "self.disclosed = []", + "occurrence": 1 + } + ] + }, + { + "mutation": "pool keeps points a disclosure rules out", + "replacements": [ + { + "old": " pool.keep(system)\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "relaxation margin loosened to 8.5", + "replacements": [ + { + "old": "relaxed > DISCLOSURE_THRESHOLD - 0.5", + "new": "relaxed > DISCLOSURE_THRESHOLD - 1.5", + "occurrence": 1 + } + ] + }, + { + "mutation": "a timed-out search proves absence", + "replacements": [ + { + "old": "proven_absent = proven_absent and status == 2", + "new": "proven_absent = proven_absent and status in (1, 2)", + "occurrence": 1 + } + ] + }, + { + "mutation": "solver witnesses used without the exact check", + "replacements": [ + { + "old": " if not self.feasible(sizes):\n return None\n", + "new": " if False:\n return None\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "positive solver gap", + "replacements": [ + { + "old": "\"mip_rel_gap\": gap,", + "new": "\"mip_rel_gap\": 0.01,", + "occurrence": 1 + } + ] + }, + { + "mutation": "atoms assumed nonempty", + "replacements": [ + { + "old": " self.bounds = Bounds(\n 0,", + "new": " self.bounds = Bounds(\n np.concatenate([np.ones(self.atoms), np.zeros(binaries)]),", + "occurrence": 1 + } + ] + }, + { + "mutation": "relations registered whether or not guaranteed", + "replacements": [ + { + "old": " if guaranteed:\n registry.relate(", + "new": " if True:\n registry.relate(", + "occurrence": 1 + } + ] + }, + { + "mutation": "a violated relation tolerated", + "replacements": [ + { + "old": " raise ValueError(f\"a guaranteed relation fails: {kind} {left}\")", + "new": " continue", + "occurrence": 1 + } + ] + }, + { + "mutation": "relations dropped from the audit", + "replacements": [ + { + "old": " relations = model_relations(registry, model)\n", + "new": " relations = model_relations(registry, model)[:0]\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "public block reports the audited target count", + "replacements": [ + { + "old": " \"status\": audit[\"status\"],\n }", + "new": " \"status\": audit[\"status\"],\n \"audited\": len(audit[\"targets\"]),\n }", + "occurrence": 1 + } + ] + }, + { + "mutation": "fractional ages accepted", + "replacements": [ + { + "old": "numeric.between(1, age67._MAX_AGE_CODE) & numeric.mod(1).eq(0)", + "new": "numeric.between(1, age67._MAX_AGE_CODE)", + "occurrence": 1 + } + ] + }, + { + "mutation": "negative coarsening input accepted", + "replacements": [ + { + "old": "if value < 0 or not np.isfinite(value):", + "new": "if not np.isfinite(value):", + "occurrence": 1 + } + ] + }, + { + "mutation": "nonfinite coarsening input accepted", + "replacements": [ + { + "old": "if value < 0 or not np.isfinite(value):", + "new": "if value < 0:", + "occurrence": 1 + } + ] + }, + { + "mutation": "output replaces an existing file", + "replacements": [ + { + "old": " os.link(temporary, path)\n", + "new": " os.replace(temporary, path)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "output not flushed to disk", + "replacements": [ + { + "old": " view = view[os.write(descriptor, view) :]\n os.fsync(descriptor)\n", + "new": " view = view[os.write(descriptor, view) :]\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a short write left incomplete", + "replacements": [ + { + "old": " while view:\n view = view[os.write(descriptor, view) :]\n", + "new": " os.write(descriptor, view)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "attempt record not flushed to disk", + "replacements": [ + { + "old": " stream.flush()\n os.fsync(stream.fileno())\n", + "new": " stream.flush()\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's sets left out of the model", + "replacements": [ + { + "old": " for path, (unit_type, units, _) in registry.prior.items():\n grouped.setdefault((unit_type, units), []).append(path)\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's counts bound their sets only from below", + "replacements": [ + { + "old": " lower[row] = max(lower[row], value)\n upper[row] = min(upper[row], value)\n", + "new": " lower[row] = max(lower[row], value)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "targets are the refined atoms, not this release's classes", + "replacements": [ + { + "old": " rows = tuple(row for row in signature if current[row])\n", + "new": " rows = tuple(signature)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "differences of two B0.1 sets audited as targets", + "replacements": [ + { + "old": " and not (current[subset] and current[superset])\n ):\n", + "new": " and False\n ):\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a B0.1 count not checked against its set", + "replacements": [ + { + "old": " if value != len(units):\n", + "new": " if False:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's rerun not compared with its record", + "replacements": [ + { + "old": " if rerun != counts:\n", + "new": " if False:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's sums not checked", + "replacements": [ + { + "old": " if value != sum(leaves[part] for part in sums[path]):\n", + "new": " if False:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a modeled set B0.1 never published accepted", + "replacements": [ + { + "old": " if missing := set(sets) - set(leaves):\n", + "new": " if missing := set():\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's ledger copies not checked", + "replacements": [ + { + "old": " if copied != expected:\n", + "new": " if False:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a change pair's row count registered with its persons added", + "replacements": [ + { + "old": " value = sum(sign * _cell(counts, part) for sign, part in terms)\n", + "new": " value = sum(_cell(counts, part) for sign, part in terms)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's sets not registered in main()", + "replacements": [ + { + "old": " register_b0_1(registry, earlier, committed[\"counts\"])\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's record not checked before the audit", + "replacements": [ + { + "old": " check_b0_1_record(\n committed,\n b0_1_structural_counts(person_wave, validity_by_wave),\n earlier,\n )\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's age support ends a year early", + "replacements": [ + { + "old": " in_support=valid[\"age\"].between(low, high).to_numpy(),\n", + "new": " in_support=valid[\"age\"].between(low, high - 1).to_numpy(),\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's dispositions inverted", + "replacements": [ + { + "old": " [person for person, label in labels.items() if label == name],\n", + "new": " [person for person, label in labels.items() if label != name],\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an empty pool classified as wide", + "replacements": [ + { + "old": " if pool.size == 0:\n return {**record, \"result\": \"narrow\", \"proof\": \"undetermined\"}, None\n", + "new": " if pool.size == 0:\n return {**record, \"result\": \"wide\", \"proof\": \"pool\"}, None\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an emptied pool not reseeded", + "replacements": [ + { + "old": " if reseeded:\n # The disclosures ruled out every point: seed again under them.\n seed_pool(system, pool, seconds=seconds)\n", + "new": " if reseeded:\n # The disclosures ruled out every point: seed again under them.\n pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an empty reseeding releases", + "replacements": [ + { + "old": " seed_pool(system, pool, seconds=seconds)\n if pool.size == 0:\n status = \"withheld\"\n", + "new": " seed_pool(system, pool, seconds=seconds)\n if pool.size == 0:\n status = \"released\"\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "seed directions drawn without the fixed seed", + "replacements": [ + { + "old": " generator = np.random.default_rng(POOL_SEED)\n", + "new": " generator = np.random.default_rng()\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "targets involving B0.1's sets marked as this release's", + "replacements": [ + { + "old": " \"earlier\": not (current[superset] and current[subset]),\n", + "new": " \"earlier\": False,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy writes atom columns", + "replacements": [ + { + "old": " {key: target[key] for key in TARGET_FIELDS if key in target}\n", + "new": " {key: target[key] for key in (*TARGET_FIELDS, \"columns\") if key in target}\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy sums every target into class sizes", + "replacements": [ + { + "old": " if target[\"kind\"] == \"atom\":\n class_sizes.setdefault(", + "new": " if True:\n class_sizes.setdefault(", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's households taken at each row's wave", + "replacements": [ + { + "old": " home[person] for person in persons if pd.notna(home[person])\n", + "new": " person for person in persons if pd.notna(home[person])\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy writes the atom sizes", + "replacements": [ + { + "old": " \"class_sizes\": class_sizes,\n }\n", + "new": " \"class_sizes\": class_sizes,\n \"atom_sizes\": audit[\"atom_sizes\"],\n }\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the audit prints the atom sizes", + "replacements": [ + { + "old": " truth = model[\"sizes\"]\n", + "new": " truth = model[\"sizes\"]\n print(truth.tolist(), file=sys.stderr)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a failure keeps a library's message", + "replacements": [ + { + "old": " return f\"B0.2 stopped in {where}: {type(error).__name__}\"\n", + "new": " return f\"B0.2 stopped in {where}: {type(error).__name__}: {error}\"\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "warnings shown inside the guard", + "replacements": [ + { + "old": " warnings.simplefilter(\"ignore\")\n yield\n", + "new": " warnings.simplefilter(\"default\")\n yield\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the solver prints its log", + "replacements": [ + { + "old": " \"disp\": False,\n", + "new": " \"disp\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the rerun gate skips the rebuilt sets' sizes", + "replacements": [ + { + "old": " if len(units) != sum(sign * leaves[part] for sign, part in terms):\n", + "new": " if False:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "differences found in row order, not this release's first", + "replacements": [ + { + "old": " pairs.sort(key=lambda pair: not (current[pair[0]] and current[pair[1]]))\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "two of this release's sets that B0.1 also published make no target", + "replacements": [ + { + "old": " and earlier[superset]\n and not (current[subset] and current[superset])\n ):\n", + "new": " and earlier[superset]\n ):\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the program limit back at 60 seconds", + "replacements": [ + { + "old": "PROGRAM_SECONDS = 300.0\n", + "new": "PROGRAM_SECONDS = 60.0\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the guard catches only Exception", + "replacements": [ + { + "old": " except BaseException as error:\n failure = _failure_text(error, phase)\n", + "new": " except Exception as error:\n failure = _failure_text(error, phase)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy writes the rounds", + "replacements": [ + { + "old": " \"status\": audit[\"status\"],\n \"targets\": targets,\n", + "new": " \"status\": audit[\"status\"],\n \"rounds\": audit[\"rounds\"],\n \"targets\": targets,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy writes each target's settling round", + "replacements": [ + { + "old": " \"true_size\",\n \"result\",\n)\n", + "new": " \"true_size\",\n \"result\",\n \"round\",\n)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "provenance gains a figure from the panel", + "replacements": [ + { + "old": " \"worktree_clean\": True,\n", + "new": " \"worktree_clean\": True,\n \"rows_read\": len(person_wave),\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a failure in the release prints its type and line", + "replacements": [ + { + "old": " if phase not in LOCATED_PHASES:\n return f\"B0.2 stopped in {RELEASE}\"\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "the guard skips the descriptor redirect", + "replacements": [ + { + "old": " os.dup2(null, 1)\n os.dup2(null, 2)\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the guard skips the C library's flush", + "replacements": [ + { + "old": " ctypes.CDLL(None).fflush(None)\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the guard leaves Python's streams unredirected", + "replacements": [ + { + "old": " with redirect_stdout(python_null), redirect_stderr(python_null):\n yield\n", + "new": " yield\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the guard restores the descriptors before flushing", + "replacements": [ + { + "old": " try:\n _flush_streams()\n finally:\n os.dup2(saved[0], 1)\n os.dup2(saved[1], 2)\n", + "new": " try:\n os.dup2(saved[0], 1)\n os.dup2(saved[1], 2)\n finally:\n _flush_streams()\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "main runs the release in the reading phase", + "replacements": [ + { + "old": " with count_free_failures(RELEASE):\n", + "new": " with count_free_failures(READING):\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the script's own raise in the release loses its message", + "replacements": [ + { + "old": " if raised_here:\n where", + "new": " if raised_here and phase in LOCATED_PHASES:\n where", + "occurrence": 1 + } + ] + }, + { + "mutation": "the load check reads the one-minute load", + "replacements": [ + { + "old": " os.getloadavg()[1],\n", + "new": " os.getloadavg()[0],\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the AC power check is dropped", + "replacements": [ + { + "old": " \"ac_power\": self.host.power_source() == AC_POWER,\n", + "new": " \"ac_power\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the sleep-assertion check is dropped", + "replacements": [ + { + "old": " \"sleep_assertions\": all(\n", + "new": " \"sleep_assertions\": True or all(\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the nice check is dropped", + "replacements": [ + { + "old": " \"nice\": niceness == 0,\n", + "new": " \"nice\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the background-policy check is dropped", + "replacements": [ + { + "old": " \"background\": background == 0,\n", + "new": " \"background\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the QoS check is dropped", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the load check is dropped", + "replacements": [ + { + "old": " \"load\": load < cpus,\n", + "new": " \"load\": True,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the QoS check passes a background clamp", + "replacements": [ + { + "old": "QOS_CLASS_DEFAULT = 0x15\n", + "new": "QOS_CLASS_DEFAULT = 0x09\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the QoS check passes an unknown class below the default", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": qos not in (0x11, 0x09, 0x05),\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the QoS check fails an unspecified class", + "replacements": [ + { + "old": " \"qos\": qos == QOS_CLASS_UNSPECIFIED or qos >= QOS_CLASS_DEFAULT,\n", + "new": " \"qos\": qos >= QOS_CLASS_DEFAULT,\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a lapsed sleep assertion is not taken again", + "replacements": [ + { + "old": " self.assertions[kind] = self.host.hold(kind)\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the sleep assertions are taken only after the attempt record", + "replacements": [ + { + "old": " conditions = _start_conditions()\n _start_attempt(local, repository_head, script_sha256)\n", + "new": " _start_attempt(local, repository_head, script_sha256)\n conditions = _start_conditions()\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the run conditions are checked only at the start", + "replacements": [ + { + "old": " self.before_program()\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a failing condition stops the run at once", + "replacements": [ + { + "old": " if rechecks == RECHECKS:\n", + "new": " if rechecks == 0:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the wait stops one recheck early", + "replacements": [ + { + "old": " if rechecks == RECHECKS:\n", + "new": " if rechecks == RECHECKS - 1:\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "rechecks do not wait", + "replacements": [ + { + "old": " time.sleep(RECHECK_SECONDS)\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an unreached target keeps an earlier round's result", + "replacements": [ + { + "old": " outcome[\"round\"] == ending\n", + "new": " True\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a class names no sets", + "replacements": [ + { + "old": " for key in (\"sets\", \"superset\", \"subset\")\n", + "new": " for key in (\"superset\", \"subset\")\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a class's sets include B0.1's", + "replacements": [ + { + "old": " list(path) for path in registry.sets if path_rows[path] in held\n", + "new": " list(path) for path in path_rows if path_rows[path] in held\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the identifier check is dropped", + "replacements": [ + { + "old": " _require_unit_identifiers(person_wave, anchor, design)\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "the identifier check counts every row", + "replacements": [ + { + "old": " rows = {\"person-wave\": _present(person_wave), \"anchor\": _present(anchor)}\n", + "new": " rows = {\"person-wave\": person_wave, \"anchor\": anchor}\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the audit saves the atom sizes to a file in the temporary directory (np.save)", + "replacements": [ + { + "old": " truth = model[\"sizes\"]\n", + "new": " truth = model[\"sizes\"]\n np.save(os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), \"b02_atoms.npy\"), truth)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy marks a result settled after round 1 with a trailing dot", + "replacements": [ + { + "old": " {key: target[key] for key in TARGET_FIELDS if key in target}\n", + "new": " {**{key: target[key] for key in TARGET_FIELDS if key in target}, **({\"result\": target[\"result\"] + \".\"} if target.get(\"round\", 1) > 1 else {})}\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the local copy orders this release's targets by settling round", + "replacements": [ + { + "old": " for target in audit[\"targets\"]\n if not target[\"earlier\"]\n", + "new": " for target in sorted(audit[\"targets\"], key=lambda t: -t.get(\"round\", 0))\n if not target[\"earlier\"]\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "B0.1's household sets are not registered", + "replacements": [ + { + "old": " for path, (unit_type, units, terms) in sets.items():\n value = sum(", + "new": " for path, (unit_type, units, terms) in sets.items():\n if unit_type != \"persons\":\n continue\n value = sum(", + "occurrence": 1 + } + ] + }, + { + "mutation": "an assertion at the off level counts as held", + "replacements": [ + { + "old": " and level.value == _ASSERTION_ON\n", + "new": " or True\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an assertion at the off level is not given back before it is taken again", + "replacements": [ + { + "old": " # One that exists at the off level is given back first.\n self.host.release(assertion)\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "the assertion check does not give back the properties it copies", + "replacements": [ + { + "old": " self.cf.CFRelease(properties)\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "the identifier check does not require a design stratum", + "replacements": [ + { + "old": " complete = design.dropna(subset=[\"person_id\", \"stratum\", \"cluster\"])\n", + "new": " complete = design.dropna(subset=[\"person_id\", \"cluster\"])\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the exemption is never applied", + "replacements": [ + { + "old": " outcome = {**outcome, \"result\": \"exempt\"}\n", + "new": " pass\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the exemption compares against the round's own programs", + "replacements": [ + { + "old": " if outcome[\"result\"] == \"narrow\" and exempt(\n earlier, targets[index], outcome, seconds=seconds\n ):", + "new": " if outcome[\"result\"] == \"narrow\" and exempt(\n earlier and (system, earlier[1]),\n targets[index],\n outcome,\n seconds=seconds,\n ):", + "occurrence": 1 + } + ] + }, + { + "mutation": "only a target of true size 1 to 9 can be exempt", + "replacements": [ + { + "old": " if earlier is None or outcome.get(\"proof\") != \"optima\":\n return False\n", + "new": " if earlier is None or outcome.get(\"proof\") != \"optima\":\n return False\n if target[\"true_size\"] >= DISCLOSURE_THRESHOLD:\n return False\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a target holding an atom no B0.1 set counts can be exempt", + "replacements": [ + { + "old": " if uncounted[columns].any():\n return False\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "an unproven exemption program counts as exempt", + "replacements": [ + { + "old": " return False\n (maximum, _), (minimum, _) = extremes\n", + "new": " return True\n (maximum, _), (minimum, _) = extremes\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "a range within B0.1's, not equal to it, is exempt", + "replacements": [ + { + "old": " return [minimum, maximum] == outcome[\"range\"]\n", + "new": " low, high = outcome[\"range\"]\n return minimum <= low and high <= maximum\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the exemption programs both maximize", + "replacements": [ + { + "old": " for sense in (1, -1)\n", + "new": " for sense in (1, 1)\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "the exemption reads its least and greatest sizes swapped", + "replacements": [ + { + "old": " (maximum, _), (minimum, _) = extremes\n return", + "new": " (minimum, _), (maximum, _) = extremes\n return", + "occurrence": 1 + } + ] + }, + { + "mutation": "the exemption programs are not recorded", + "replacements": [ + { + "old": " outcome[\"programs\"].extend([\"earlier maximum\", \"earlier minimum\"])\n", + "new": "", + "occurrence": 1 + } + ] + }, + { + "mutation": "an exempt target is disclosed", + "replacements": [ + { + "old": " settled[index] = {**outcome, \"round\": len(rounds) + 1}\n if outcome[\"result\"] != \"narrow\":\n", + "new": " settled[index] = {**outcome, \"round\": len(rounds) + 1}\n if outcome[\"result\"] == \"exempt\":\n added.append(index)\n continue\n if outcome[\"result\"] != \"narrow\":\n", + "occurrence": 1 + } + ] + }, + { + "mutation": "an exempt target is not tested again", + "replacements": [ + { + "old": " if settled.get(index, {}).get(\"result\") == \"irrelevant\":\n", + "new": " if settled.get(index, {}).get(\"result\") in (\n \"irrelevant\",\n \"exempt\",\n ):\n", + "occurrence": 1 + } + ] + } +] diff --git a/docs/design/track_b_b0_2_evidence/mutations.py b/docs/design/track_b_b0_2_evidence/mutations.py new file mode 100644 index 00000000..8080238e --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/mutations.py @@ -0,0 +1,182 @@ +"""Run B0.2's mutation checks against the count script's full test module. + +Each check in ``mutation_specs.json`` makes one deliberate change to +``scripts/track_b_b0_2_counts.py``, runs all of +``tests/test_track_b_b0_2_counts.py`` in a fresh pytest process with its +own Hypothesis database, then restores the script's bytes and verifies +their SHA-256. A check is killed when pytest exits nonzero. The script +must be unmodified in git before the run starts. The record is written in +the format of ``docs/design/track_b_b0_2_counts_mutations.json``. + +Run from the root of a clean clone, with the interpreter that runs the +tests:: + + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE \\ + PYTHONDONTWRITEBYTECODE=1 \\ + python docs/design/track_b_b0_2_evidence/mutations.py RECORD +""" + +from __future__ import annotations + +import hashlib +import json +import os +import re +import subprocess +import sys +import tempfile +import time +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[2] +TARGET = ROOT / "scripts/track_b_b0_2_counts.py" +PROTOCOL = ROOT / "docs/design/track_b_b0_2_protocol.md" +TESTS = "tests/test_track_b_b0_2_counts.py" +ARGUMENTS = ["-m", "pytest", "-q", "-rf", "-p", "no:cacheprovider", TESTS] +FAILED = re.compile(r"^FAILED (\S+?\[[^\]]*\]|\S+)") +TIMEOUT = 1800 + + +def sha256(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def protocol_sha256() -> str: + text = PROTOCOL.read_bytes() + begin, end = b"", b"" + return sha256(text[text.index(begin) : text.index(end) + len(end)]) + + +def mutate(text: str, replacements) -> tuple[str, list[int]]: + anchors = [] + for replacement in replacements: + old, new = replacement["old"], replacement["new"] + count = text.count(old) + if count == 0: + raise SystemExit(f"anchor missing: {old!r}") + anchors.append(count) + if replacement["occurrence"] == "all": + text = text.replace(old, new) + else: + start = -1 + for _ in range(replacement["occurrence"]): + start = text.index(old, start + 1) + text = text[:start] + new + text[start + len(old) :] + return text, anchors + + +def main(record_path: Path) -> None: + dirty = subprocess.run( + ["git", "status", "--porcelain", "--", str(TARGET)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ).stdout + if dirty: + raise SystemExit( + "the count script is modified; run from a clean clone" + ) + specs = json.loads((HERE / "mutation_specs.json").read_text()) + original = TARGET.read_bytes() + original_sha = sha256(original) + environment = dict( + os.environ, + OMP_NUM_THREADS="1", + OPENBLAS_NUM_THREADS="1", + OMP_WAIT_POLICY="PASSIVE", + PYTHONDONTWRITEBYTECODE="1", + PYTHONPATH=f"{ROOT / 'src'}:{ROOT}", + ) + checks = [] + for number, spec in enumerate(specs, 1): + text, anchors = mutate(original.decode(), spec["replacements"]) + started = time.monotonic() + try: + TARGET.write_bytes(text.encode()) + with tempfile.TemporaryDirectory() as storage: + run = subprocess.run( + [sys.executable, *ARGUMENTS], + cwd=ROOT, + capture_output=True, + text=True, + env=dict( + environment, HYPOTHESIS_STORAGE_DIRECTORY=storage + ), + timeout=TIMEOUT, + ) + code, output = run.returncode, run.stdout + run.stderr + except subprocess.TimeoutExpired as error: + stdout = error.stdout + code = "timeout" + output = stdout if isinstance(stdout, str) else "" + finally: + TARGET.write_bytes(original) + restored = sha256(TARGET.read_bytes()) + if restored != original_sha: + raise SystemExit("restore failed") + lines = output.strip().splitlines() + failed = sorted( + {match.group(1) for line in lines if (match := FAILED.match(line))} + ) + status = "killed" if code != 0 else "survived" + checks.append( + { + "mutation": spec["mutation"], + "replacements": [ + dict(replacement, anchor_occurrences=count) + for replacement, count in zip( + spec["replacements"], anchors, strict=True + ) + ], + "exit_code": code, + "failed_tests": failed, + "summary": lines[-1] if lines else "", + "status": status, + "restored": True, + "restored_source_sha256": restored, + "elapsed_seconds": round(time.monotonic() - started, 1), + } + ) + print( + f"{number:2d} {status:8s} {len(failed):3d} {spec['mutation']}" + f" | {checks[-1]['summary']}", + flush=True, + ) + killed = sum(check["status"] == "killed" for check in checks) + record = { + "schema_version": 1, + "verification_class": "structural-count protocol conformance", + "admitted_scope": "Invented frames only; no real-data run.", + "mutation_method": ( + "One deliberate production behavior change per check, from " + "docs/design/track_b_b0_2_evidence/mutation_specs.json. Each " + "check runs the whole count test module in a fresh pytest " + "process with its own Hypothesis database; a nonzero exit " + "kills it. The original source bytes are restored and their " + "SHA-256 verified after every check. No mutated " + "implementation is committed." + ), + "protocol_sha256": protocol_sha256(), + "restored_source_sha256": { + "scripts/track_b_b0_2_counts.py": original_sha + }, + "result": f"{killed}/{len(checks)} mutations killed", + "runner_command": ( + "OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 " + "OMP_WAIT_POLICY=PASSIVE PYTHONDONTWRITEBYTECODE=1 " + ".venv/bin/python docs/design/track_b_b0_2_evidence/" + "mutations.py RECORD" + ), + "test_command": ( + "PYTHONPATH=$PWD/src:$PWD python " + " ".join(ARGUMENTS) + ), + "checks": checks, + } + record_path.write_text(json.dumps(record, indent=2) + "\n") + print(f"FINAL: {record['result']}; restored {original_sha}") + + +if __name__ == "__main__": + main(Path(sys.argv[1])) diff --git a/docs/design/track_b_b0_2_evidence/oracle.py b/docs/design/track_b_b0_2_evidence/oracle.py new file mode 100644 index 00000000..89d914cf --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/oracle.py @@ -0,0 +1,149 @@ +"""An enumeration oracle for B0.2's disclosure rule. + +INVENTED TABLES ONLY. This module reads no data. It restates the rule in +the protocol by brute force, without the count script's integer programs, +so the searches beside it can check the script's guarantees on tables too +large for the unit tests. + +A table is ``(rows, sizes)``. Each row is a tuple of atom indices, one per +published set, and ``sizes`` holds the true atom sizes. The reader knows +the rows and their coarsened totals, and atoms may be empty, as in the +protocol's reader model. There is one unit type and no registered +relation beyond the rows themselves. +""" + +from __future__ import annotations + +import numpy as np + +BASE = 10 +THRESHOLD = 10 +WIDTH = 4 + + +def coarsen(value: int) -> int: + """Publish 0 as 0, 1 to 9 as ``-1`` (below the base), and round + everything else to the nearest multiple of the base, halves up.""" + if value == 0: + return 0 + if value < BASE: + return -1 + return (2 * value + BASE) // (2 * BASE) * BASE + + +def upper(published: int) -> int: + """The largest count that publishes as ``published``.""" + return max( + value + for value in range(max(published, 0) + BASE + 1) + if coarsen(value) == published + ) + + +def targets(rows, atoms: int) -> list[tuple[int, ...]]: + """Every atom, and every difference of two nested published sets.""" + found = {(atom,) for atom in range(atoms)} + sets = [frozenset(row) for row in rows] + for big in sets: + for small in sets: + if small and small < big: + found.add(tuple(sorted(big - small))) + return sorted(found) + + +def coarsen_array(values: np.ndarray) -> np.ndarray: + """``coarsen`` applied to every element of an integer array.""" + rounded = (2 * values + BASE) // (2 * BASE) * BASE + return np.where(values == 0, 0, np.where(values < BASE, -1, rounded)) + + +def candidates(rows, sizes, limit=None, chunk=1_000_000): + """Every atom-size vector whose publication equals the truth's. + + Vectors are listed in lexicographic order, the first atom slowest, by + decoding consecutive integers in chunks, so memory stays bounded + however many vectors the caps allow. With ``limit``, returns None as + soon as more than ``limit`` vectors are found. + """ + atoms = len(sizes) + published = np.array([coarsen(sum(sizes[a] for a in row)) for row in rows]) + caps = [ + min(upper(published[j]) for j, row in enumerate(rows) if a in row) + for a in range(atoms) + ] + radices = np.array([cap + 1 for cap in caps], dtype=np.int64) + strides = np.ones(atoms, dtype=np.int64) + for a in range(atoms - 2, -1, -1): + strides[a] = strides[a + 1] * radices[a + 1] + total = int(np.prod(radices)) + matrix = np.array([[int(a in row) for a in range(atoms)] for row in rows]) + found = [] + count = 0 + for start in range(0, total, chunk): + index = np.arange(start, min(start + chunk, total), dtype=np.int64) + grid = index[:, None] // strides % radices + keep = np.all(coarsen_array(grid @ matrix.T) == published, axis=1) + found.append(grid[keep]) + count += int(keep.sum()) + if limit is not None and count > limit: + return None + return np.concatenate(found) + + +def classify(values: np.ndarray) -> str: + """Wide if the values span the protection width; otherwise irrelevant + if none lies in 1 to 9, and narrow if one does.""" + if values.size == 0: + raise AssertionError("empty system") + if values.max() - values.min() >= WIDTH: + return "wide" + if not np.any((values >= 1) & (values < THRESHOLD)): + return "irrelevant" + return "narrow" + + +def release(rows, sizes, grid=None, target_list=None): + """Apply the rule by enumeration. + + Returns the status, the targets disclosed in closure rounds, and the + mask of candidate vectors left after the disclosures. + """ + if grid is None: + grid = candidates(rows, sizes) + if target_list is None: + target_list = targets(rows, len(sizes)) + values = {t: grid[:, list(t)].sum(axis=1) for t in target_list} + truth = {t: sum(sizes[a] for a in t) for t in target_list} + mask = np.ones(len(grid), dtype=bool) + disclosed = [] + while True: + narrow = [ + t + for t in target_list + if t not in disclosed and classify(values[t][mask]) == "narrow" + ] + if any(1 <= truth[t] < THRESHOLD for t in narrow): + return "withheld", disclosed, mask + if not narrow: + return "released", disclosed, mask + disclosed.extend(narrow) + for t in narrow: + mask &= ~((values[t] >= 1) & (values[t] < THRESHOLD)) + + +def reader(rows, sizes, grid=None): + """The reader's posterior after a release. + + Returns the candidate grid, the targets, and a mask of the candidate + vectors whose own run of the rule releases. + """ + if grid is None: + grid = candidates(rows, sizes) + target_list = targets(rows, len(sizes)) + released = np.array( + [ + release(rows, list(point), grid, target_list)[0] == "released" + for point in grid + ] + ) + return grid, target_list, released diff --git a/docs/design/track_b_b0_2_evidence/ranges.py b/docs/design/track_b_b0_2_evidence/ranges.py new file mode 100644 index 00000000..210e8c39 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/ranges.py @@ -0,0 +1,73 @@ +"""Check whether a sensitive target's possible sizes leave gaps. + +INVENTED TABLES ONLY. The protocol's rule is a range: a released table's +sensitive targets each take sizes at least 4 apart among the vectors a +reader cannot rule out. It does not promise every size in between. This +script counts, over random released tables, the sensitive targets whose +possible sizes skip a value in that range, and those with fewer than five +possible sizes. Each trial draws two to four atoms, two to six published +rows over them (a repeated row counts once) and atom sizes from 1 to 16, +and keeps the table if every atom has a distinct nonempty signature and +the reader has at most 4,000 candidate vectors. + +Run from the repository root:: + + python docs/design/track_b_b0_2_evidence/ranges.py SEED TRIALS + +The output for seed 5 and 3,000 trials is committed as ``ranges.txt``. +""" + +from __future__ import annotations + +import random +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import oracle # noqa: E402 + + +def main(seed: int, trials: int) -> None: + rng = random.Random(seed) + stats = dict.fromkeys(["released", "sensitive", "gaps", "under5"], 0) + for _ in range(trials): + atoms = rng.choice([2, 3, 3, 4]) + rows = set() + for _ in range(rng.randint(2, 6)): + size = rng.randint(1, atoms) + rows.add(tuple(sorted(rng.sample(range(atoms), size)))) + rows = sorted(rows) + signatures = [ + frozenset(j for j, row in enumerate(rows) if a in row) + for a in range(atoms) + ] + if not all(signatures) or len(set(signatures)) != atoms: + continue + sizes = [rng.randint(1, 16) for _ in range(atoms)] + grid = oracle.candidates(rows, sizes, limit=4000) + if grid is None: + continue + target_list = oracle.targets(rows, atoms) + status, disclosed, mask = oracle.release( + rows, sizes, grid, target_list + ) + if status != "released": + continue + stats["released"] += 1 + for t in target_list: + if not 1 <= sum(sizes[a] for a in t) < oracle.THRESHOLD: + continue + stats["sensitive"] += 1 + values = sorted(set(grid[mask][:, list(t)].sum(axis=1).tolist())) + if len(values) != values[-1] - values[0] + 1: + stats["gaps"] += 1 + print("GAP", rows, sizes, t, values, disclosed) + if len(values) < 5: + stats["under5"] += 1 + print("UNDER5", rows, sizes, t, values, disclosed) + print(stats) + + +if __name__ == "__main__": + main(int(sys.argv[1]), int(sys.argv[2])) diff --git a/docs/design/track_b_b0_2_evidence/ranges.txt b/docs/design/track_b_b0_2_evidence/ranges.txt new file mode 100644 index 00000000..3175515f --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/ranges.txt @@ -0,0 +1 @@ +{'released': 1635, 'sensitive': 2665, 'gaps': 0, 'under5': 0} diff --git a/docs/design/track_b_b0_2_evidence/search.py b/docs/design/track_b_b0_2_evidence/search.py new file mode 100644 index 00000000..5d8585e1 --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/search.py @@ -0,0 +1,117 @@ +"""Random search of larger tables against the protocol's guarantees. + +INVENTED TABLES ONLY. Each trial draws three to five atoms and three to +six published rows over them (a repeated row counts once), then a size for +each atom from ``SIZES`` (1 to 14) or zero. It keeps the table if no atom +is empty, every atom has a distinct nonempty signature, and the reader has +at most 6,000 candidate vectors. For each released table it checks, by +enumeration with ``oracle``: + +- the replay theorem: the reader's posterior (every candidate whose own + run of the rule releases) equals the candidates left after the rounds; +- the width guarantee: every target of true size 1 to 9 spans at least + the protection width over that posterior; +- the closure rounds' necessity: whether a rule that stopped after its + first round would leave a sensitive target narrow. + +Run from the repository root:: + + python docs/design/track_b_b0_2_evidence/search.py SEED TRIALS + +The output for seed 2 and 1,500 trials is committed as ``search.txt``. +""" + +from __future__ import annotations + +import random +import sys +from pathlib import Path + +import numpy as np + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import oracle # noqa: E402 + +SIZES = [1, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 14] + + +def single_round(rows, sizes, grid, target_list) -> bool: + """Whether a rule with no closure round releases this vector.""" + values = {t: grid[:, list(t)].sum(axis=1) for t in target_list} + narrow = [t for t in target_list if oracle.classify(values[t]) == "narrow"] + return not any( + 1 <= sum(sizes[a] for a in t) < oracle.THRESHOLD for t in narrow + ) + + +def narrow_sensitive(sizes, grid, mask, target_list): + """The sensitive targets that span less than the protection width.""" + found = [] + for t in target_list: + if 1 <= sum(sizes[a] for a in t) < oracle.THRESHOLD: + values = grid[mask][:, list(t)].sum(axis=1) + if values.max() - values.min() < oracle.WIDTH: + found.append((t, sorted(set(values.tolist())))) + return found + + +def main(seed: int, trials: int) -> None: + rng = random.Random(seed) + stats = dict.fromkeys( + [ + "tables", + "released", + "multi_round", + "theorem_fail", + "width_fail", + "single_unsound", + ], + 0, + ) + for _ in range(trials): + atoms = rng.choice([3, 3, 4, 4, 5]) + rows = set() + for _ in range(rng.randint(3, 6)): + size = rng.randint(1, atoms) + rows.add(tuple(sorted(rng.sample(range(atoms), size)))) + rows = sorted(rows) + signatures = [ + frozenset(j for j, row in enumerate(rows) if a in row) + for a in range(atoms) + ] + if not all(signatures) or len(set(signatures)) != atoms: + continue + sizes = [rng.choice([0, *SIZES]) for _ in range(atoms)] + if 0 in sizes: + continue + grid = oracle.candidates(rows, sizes, limit=6000) + if grid is None: + continue + grid, target_list, released = oracle.reader(rows, sizes, grid) + stats["tables"] += 1 + status, disclosed, mask = oracle.release( + rows, sizes, grid, target_list + ) + if status != "released": + continue + stats["released"] += 1 + stats["multi_round"] += bool(disclosed) + if not np.array_equal(released, mask): + stats["theorem_fail"] += 1 + print("THEOREM FAIL", rows, sizes) + for found in narrow_sensitive(sizes, grid, released, target_list): + stats["width_fail"] += 1 + print("WIDTH FAIL", rows, sizes, found) + single = np.array( + [single_round(rows, list(p), grid, target_list) for p in grid] + ) + found = narrow_sensitive(sizes, grid, single, target_list) + if found: + stats["single_unsound"] += 1 + print("SINGLE-ROUND NARROW", rows, sizes, found[0]) + print(stats) + + +if __name__ == "__main__": + main(int(sys.argv[1]), int(sys.argv[2])) diff --git a/docs/design/track_b_b0_2_evidence/search.txt b/docs/design/track_b_b0_2_evidence/search.txt new file mode 100644 index 00000000..782d943a --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/search.txt @@ -0,0 +1 @@ +{'tables': 562, 'released': 531, 'multi_round': 0, 'theorem_fail': 0, 'width_fail': 0, 'single_unsound': 0} diff --git a/docs/design/track_b_b0_2_evidence/singleton.py b/docs/design/track_b_b0_2_evidence/singleton.py new file mode 100644 index 00000000..a279db2b --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/singleton.py @@ -0,0 +1,218 @@ +"""Find which published sets pin a target that withholds B0.2's release on +an invented panel. + +INVENTED DATA ONLY. ``invented_panel.py`` builds the panel from a seeded +generator; no PSID file is read. For HOUSEHOLDS and SEED, the script builds +the registry as ``calibrate.py`` does, with B0.1's sets registered at the +counts B0.1's own script gives, and runs the audit at the fixed base. Then, +for each target of true size 1 to 9 found narrow (one withholds the +release), it prints: + +- the target, the audit round that classified it, its proof and its + proven range; +- this release's sets that hold it (for a difference, its two ends); +- each B0.1 set that holds any of its atoms, with that set's size and its + exact published value; +- the same target classified under the publication with every B0.1 set + left out; with only the B0.1 sets that hold its atoms kept; with every + B0.1 set kept except those; and with each of those left out in turn. + +A target is matched across publications by its kind, unit type and this +release's sets, which do not depend on B0.1's sets (``build_model``). Each +classification uses a fresh system with no disclosures, as the audit's +first round does, and its own pool from the seed programs; so it answers +for the first round's constraints, not for a whole audit of the reduced +publication. A "wide" verdict rests on two verified integer points, and +"narrow" with proof "optima" on two proven optima. "Narrow" with proof +"undetermined" proves nothing: no proof was obtained, because a program +timed out, a search neither found a point nor proved there was none, or +the pool was empty. The pool can move a verdict between a proof and +"undetermined", never between two proofs. A narrow range of [1, 1] +identifies the target's size exactly; narrow only needs a width under 4. + +Run from the repository root, with the thread settings the protocol uses:: + + OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE \\ + PYTHONPATH=src:. python docs/design/track_b_b0_2_evidence/singleton.py \\ + HOUSEHOLDS SEED +""" + +from __future__ import annotations + +import copy +import hashlib +import sys +import time +from pathlib import Path + +ROOT = Path.cwd() +sys.path.insert(0, str(ROOT / "scripts")) +sys.path.insert(0, str(ROOT / "docs/design/track_b_b0_2_evidence")) + +import track_b_b0_2_counts as script # noqa: E402 +from invented_panel import invented_inputs # noqa: E402 + +#: At most this many B0.1 sets are left out one at a time. +SINGLE_LIMIT = 24 + + +def build(households: int, seed: int): + people, labor, anchor, design = invented_inputs(households, seed) + validity = { + wave: script.validity_frame(frame) for wave, frame in labor.items() + } + counts = script.structural_counts(people, validity, anchor, design) + registry = script.disclosure_registry(people, validity, anchor, design) + earlier = script.b0_1_sets(people, validity) + script.register_b0_1( + registry, earlier, script.b0_1_structural_counts(people, validity) + ) + return counts, registry + + +def key(target: dict) -> tuple: + return ( + target["kind"], + target["unit_type"], + tuple(tuple(path) for path in target.get("sets", ())), + tuple(target.get("superset", ())), + tuple(target.get("subset", ())), + ) + + +def name(path) -> str: + return "/".join(str(part) for part in path) + + +def classify_under(counts, registry, wanted, seconds) -> str: + started = time.monotonic() + model = script.build_model(registry) + matches = [t for t in model["targets"] if key(t) == wanted] + if len(matches) != 1: + return f"no single match ({len(matches)} targets match)" + target = matches[0] + lower, upper = script.published_bounds( + counts, registry, model, script.DISCLOSURE_BASE + ) + relations = script.model_relations(registry, model) + system = script.AuditSystem(model, lower, upper, relations, []) + if not system.feasible(model["sizes"]): + return "true sizes infeasible" + pool = script.WitnessPool(len(model["sizes"])) + script.seed_pool(system, pool, seconds=seconds) + outcome, _ = script.classify(system, target, pool, seconds=seconds) + return ( + f"{outcome['result']} (proof {outcome.get('proof')}, range" + f" {outcome.get('range')}, true size {target['true_size']}," + f" {len(target['columns'])} atoms, pool {pool.size}," + f" {time.monotonic() - started:.1f}s)" + ) + + +def main(households: int, seed: int) -> None: + seconds = script.PROGRAM_SECONDS + print( + f"singleton.py sha256 " + f"{hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}; count" + " script sha256 " + f"{hashlib.sha256(Path(script.__file__).read_bytes()).hexdigest()}", + flush=True, + ) + counts, registry = build(households, seed) + started = time.monotonic() + audit = script.audit_counts(counts, registry, script.DISCLOSURE_BASE) + print( + f"households={households} seed={seed} b0_1_sets=" + f"{len(registry.prior)} status={audit['status']}" + f" audit={time.monotonic() - started:.1f}s", + flush=True, + ) + model = script.build_model(registry) + csc = model["matrix"].tocsc() + prior_in_row: dict[int, list] = {} + for path in registry.prior: + prior_in_row.setdefault(model["path_rows"][path], []).append(path) + narrow = [ + t + for t in audit["targets"] + if t["result"] == "narrow" + and 1 <= t["true_size"] < script.DISCLOSURE_THRESHOLD + ] + for target in narrow: + print( + f"narrow target: {target['kind']} {target['unit_type']}, true" + f" size {target['true_size']}, round {target.get('round')}," + f" proof {target.get('proof')}, range {target.get('range')}," + f" {len(target['columns'])} atoms, earlier {target['earlier']}", + flush=True, + ) + if target["kind"] == "difference": + print(f" superset {name(target['superset'])}") + print(f" subset {name(target['subset'])}") + print( + " this release's sets holding it" + f" ({len(target.get('sets', ()))}):" + ) + for path in target.get("sets", ()): + print(f" {name(path)}") + rows = sorted( + { + int(row) + for column in target["columns"] + for row in csc.indices[ + csc.indptr[column] : csc.indptr[column + 1] + ] + } + ) + holding = [path for row in rows for path in prior_in_row.get(row, [])] + print(f" B0.1 sets holding any of its atoms ({len(holding)}):") + for path in holding: + unit_type, units, value = registry.prior[path] + print( + f" {name(path)}: {unit_type}, size {len(units)}," + f" published {value}" + ) + wanted = key(target) + none = copy.deepcopy(registry) + none.prior.clear() + print( + " without every B0.1 set:", + classify_under(counts, none, wanted, seconds), + flush=True, + ) + only = copy.deepcopy(registry) + for path in list(only.prior): + if path not in holding: + del only.prior[path] + print( + " with only the B0.1 sets holding it:", + classify_under(counts, only, wanted, seconds), + flush=True, + ) + rest = copy.deepcopy(registry) + for path in holding: + del rest.prior[path] + print( + " with every B0.1 set but those holding it:", + classify_under(counts, rest, wanted, seconds), + flush=True, + ) + if len(holding) > SINGLE_LIMIT: + print( + f" leaving out one at a time: the first {SINGLE_LIMIT} of" + f" {len(holding)} only" + ) + for path in holding[:SINGLE_LIMIT]: + less = copy.deepcopy(registry) + del less.prior[path] + print( + f" without {name(path)}:", + classify_under(counts, less, wanted, seconds), + flush=True, + ) + if not narrow: + print("no narrow target of true size 1 to 9") + + +if __name__ == "__main__": + main(int(sys.argv[1]), int(sys.argv[2])) diff --git a/docs/design/track_b_b0_2_evidence/singleton.txt b/docs/design/track_b_b0_2_evidence/singleton.txt new file mode 100644 index 00000000..89b4baaf --- /dev/null +++ b/docs/design/track_b_b0_2_evidence/singleton.txt @@ -0,0 +1,130 @@ +Which published sets pin the target that withholds B0.2's release on an +invented panel (INVENTED DATA ONLY; no PSID file read). +Script: docs/design/track_b_b0_2_evidence/singleton.py (SHA-256 06623013...), +loading scripts/track_b_b0_2_counts.py (7ec6ee28...) as committed at e79f41b5. +singleton.py has not changed since; the count script has, at 6cca06af (the +exemption) and 1ed6a266 (its pin). Run 2026-10-26 from a clean clone at +e79f41b5, one job at a time on a shared host, each at nice 10 on one core, with +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_WAIT_POLICY=PASSIVE +PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src:. Each program's limit is 300 s. +The first line of each run prints both scripts' SHA-256. + +$ python docs/design/track_b_b0_2_evidence/singleton.py 800 1 +singleton.py sha256 06623013c0fb45e929c0d41a098fb5baae8c61711f6aaf1d950d9bc280f507d0; count script sha256 7ec6ee2856e767c36626d3513f3abd1763de948de7d41c0cfe3219b374fe0518 +households=800 seed=1 b0_1_sets=71 status=withheld audit=281.5s +narrow target: atom persons, true size 1, round 1, proof optima, range [0, 3], 1 atoms, earlier False + this release's sets holding it (12): + rosters/R_FULL/overall/persons + rosters/R_FULL/by_age_band/1_24/persons + rosters/R_B2/overall/persons + rosters/R_B2/by_age_band/1_24/persons + b3l/rosters/R2006_FULL/overall/persons + b3l/rosters/R2006_FULL/by_age_band/1_24/persons + b3l/rosters/D2006/overall/persons + b3l/rosters/D2006/by_age_band/1_24/persons + b3l/later_support/2013/25_44/persons + b3l/later_support/2017/25_44/persons + b3l/later_support/2019/25_44/persons + b3l/later_support/2021/25_44/persons + B0.1 sets holding any of its atoms (12): + b0_1/full_anchor_persons: persons, size 1239, published 1239 + b0_1/domain_persons: persons, size 844, published 844 + b0_1/domain_weights/persons: persons, size 844, published 844 + b0_1/domain_by_anchor_wave_age/under_25: persons, size 29, published 29 + b0_1/domain_by_role/head: persons, size 624, published 624 + b0_1/scored_support/2010/valid_domain_rows_outside_age_support: persons, size 307, published 307 + b0_1/scored_support/2012/scored_rows: persons, size 454, published 454 + b0_1/domain_dispositions/2012/scored: persons, size 454, published 454 + b0_1/scored_support/2012/rows_by_cohort/prime: persons, size 247, published 247 + b0_1/level_cells/prime/persons: persons, size 270, published 270 + b0_1/level_cells/pooled/persons: persons, size 509, published 509 + b0_1/domain_dispositions/2014/present_nonpositive_weight: persons, size 20, published 20 + without every B0.1 set: wide (proof pool, range None, true size 1, 1 atoms, pool 9, 24.4s) + with only the B0.1 sets holding it: wide (proof search, range None, true size 1, 1 atoms, pool 9, 42.8s) + with every B0.1 set but those holding it: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 52.6s) + without b0_1/full_anchor_persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 66.8s) + without b0_1/domain_persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 56.4s) + without b0_1/domain_weights/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 54.4s) + without b0_1/domain_by_anchor_wave_age/under_25: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 47.0s) + without b0_1/domain_by_role/head: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 58.0s) + without b0_1/scored_support/2010/valid_domain_rows_outside_age_support: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 64.0s) + without b0_1/scored_support/2012/scored_rows: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 67.0s) + without b0_1/domain_dispositions/2012/scored: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 67.0s) + without b0_1/scored_support/2012/rows_by_cohort/prime: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 48.4s) + without b0_1/level_cells/prime/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 75.0s) + without b0_1/level_cells/pooled/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 75.1s) + without b0_1/domain_dispositions/2014/present_nonpositive_weight: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 65.3s) + +$ python docs/design/track_b_b0_2_evidence/singleton.py 3000 2 +singleton.py sha256 06623013c0fb45e929c0d41a098fb5baae8c61711f6aaf1d950d9bc280f507d0; count script sha256 7ec6ee2856e767c36626d3513f3abd1763de948de7d41c0cfe3219b374fe0518 +households=3000 seed=2 b0_1_sets=71 status=withheld audit=1157.8s +narrow target: atom persons, true size 1, round 1, proof optima, range [0, 3], 1 atoms, earlier False + this release's sets holding it (30): + rosters/R_FULL/overall/persons + rosters/R_FULL/unknown_age/persons + rosters/R_B2/overall/persons + rosters/R_B2/unknown_age/persons + b3/by_wave/2015/62_64/persons + b3/by_wave/2015/62_64/valid_rows + b3/by_wave/2015/62_64/accuracy_positive_rows + b3/by_wave/2017/65_66/persons + b3/by_wave/2017/65_66/valid_rows + b3/by_wave/2019/67_69/persons + b3/by_wave/2019/67_69/valid_rows + b3/by_wave/2019/67_69/accuracy_positive_rows + b3/distinct_persons_by_band/62_64 + b3/distinct_persons_by_band/65_66 + b3/distinct_persons_by_band/67_69 + b3/lag_pairs/2/2013_2015/62_64 + b3/lag_pairs/2/2015_2017/65_66 + b3/lag_pairs/2/2017_2019/67_69 + b3/lag_pairs/4/2011_2015/62_64 + b3/lag_pairs/4/2013_2017/65_66 + b3/lag_pairs/4/2015_2019/67_69 + b3l/rosters/R2006_FULL/overall/persons + b3l/rosters/R2006_FULL/by_age_band/45_61/persons + b3l/rosters/D2006/overall/persons + b3l/rosters/D2006/by_age_band/45_61/persons + b3l/later_support/2009/45_61/persons + b3l/later_support/2013/45_61/persons + b3l/later_support/2015/62_64/persons + b3l/later_support/2017/65_66/persons + b3l/later_support/2019/67_69/persons + B0.1 sets holding any of its atoms (17): + b0_1/full_anchor_persons: persons, size 4757, published 4757 + b0_1/domain_persons: persons, size 3138, published 3138 + b0_1/domain_weights/persons: persons, size 3138, published 3138 + b0_1/domain_by_role/head: persons, size 2269, published 2269 + b0_1/domain_by_anchor_wave_age/65_plus: persons, size 974, published 974 + b0_1/scored_support/2010/valid_domain_rows_outside_age_support: persons, size 1105, published 1105 + b0_1/scored_support/2012/scored_rows: persons, size 1770, published 1770 + b0_1/domain_dispositions/2012/scored: persons, size 1770, published 1770 + b0_1/scored_support/2012/rows_by_cohort/older: persons, size 765, published 765 + b0_1/scored_support/2014/scored_rows: persons, size 1677, published 1677 + b0_1/domain_dispositions/2014/scored: persons, size 1677, published 1677 + b0_1/scored_support/2014/rows_by_cohort/older: persons, size 745, published 745 + b0_1/scored_support/2014/assigned_or_edited_rows: persons, size 165, published 165 + b0_1/level_cells/older/persons: persons, size 907, published 907 + b0_1/level_cells/pooled/persons: persons, size 1945, published 1945 + b0_1/change_pairs/older/persons: persons, size 755, published 755 + b0_1/change_pairs/pooled_one_step/persons: persons, size 1764, published 1764 + without every B0.1 set: wide (proof search, range None, true size 1, 1 atoms, pool 9, 106.3s) + with only the B0.1 sets holding it: wide (proof pool, range None, true size 1, 1 atoms, pool 9, 103.2s) + with every B0.1 set but those holding it: wide (proof search, range None, true size 1, 1 atoms, pool 9, 133.9s) + without b0_1/full_anchor_persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 140.8s) + without b0_1/domain_persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 121.2s) + without b0_1/domain_weights/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 122.1s) + without b0_1/domain_by_role/head: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 138.3s) + without b0_1/domain_by_anchor_wave_age/65_plus: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 139.2s) + without b0_1/scored_support/2010/valid_domain_rows_outside_age_support: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 128.5s) + without b0_1/scored_support/2012/scored_rows: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 185.9s) + without b0_1/domain_dispositions/2012/scored: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 143.8s) + without b0_1/scored_support/2012/rows_by_cohort/older: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 171.0s) + without b0_1/scored_support/2014/scored_rows: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 173.8s) + without b0_1/domain_dispositions/2014/scored: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 150.6s) + without b0_1/scored_support/2014/rows_by_cohort/older: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 276.4s) + without b0_1/scored_support/2014/assigned_or_edited_rows: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 164.0s) + without b0_1/level_cells/older/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 165.8s) + without b0_1/level_cells/pooled/persons: wide (proof search, range None, true size 1, 1 atoms, pool 9, 198.1s) + without b0_1/change_pairs/older/persons: narrow (proof optima, range [0, 3], true size 1, 1 atoms, pool 9, 154.1s) + without b0_1/change_pairs/pooled_one_step/persons: wide (proof pool, range None, true size 1, 1 atoms, pool 9, 139.2s) diff --git a/docs/design/track_b_b0_2_exclusions.json b/docs/design/track_b_b0_2_exclusions.json new file mode 100644 index 00000000..3473d5e0 --- /dev/null +++ b/docs/design/track_b_b0_2_exclusions.json @@ -0,0 +1,122 @@ +{ + "added": "2026-10-23", + "q6_exclusions": [ + { + "lines": null, + "path": "docs/design/track_b_b0_2_counts.json", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_counts_mutations.json", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_counts_report.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/calibrate.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/calibration.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/exhaust_closure.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/exhaust_closure.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/invented_panel.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/mutation_specs.json", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/mutations.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/oracle.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/ranges.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/ranges.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/search.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/search.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/singleton.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_evidence/singleton.txt", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_exposure_inventory.json", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_landscape.md", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_plan.md", + "scope": "whole_file" + }, + { + "lines": null, + "path": "docs/design/track_b_b0_2_protocol.md", + "scope": "whole_file" + }, + { + "lines": null, + "path": "scripts/track_b_b0_2_counts.py", + "scope": "whole_file" + }, + { + "lines": null, + "path": "tests/test_track_b_b0_2_counts.py", + "scope": "whole_file" + } + ], + "reason": "The dynamics coordinator separated B0.2 from the B2 successor design on 2026-10-21: successor-design roles may not read B0.2's work (PR #554 beyond its title). These are B0.2's files, including the structural counts and exposure inventory it will add; the counts must be listed here before they reach master. No exposure-inventory scan lists them, so this standing record excludes them from every blinded review root that scripts/track_b_b0_1_review_root.py builds.", + "schema": "track_b_b0_2_exclusions.v1" +} diff --git a/docs/design/track_b_b0_2_landscape.md b/docs/design/track_b_b0_2_landscape.md new file mode 100644 index 00000000..fffdbf9d --- /dev/null +++ b/docs/design/track_b_b0_2_landscape.md @@ -0,0 +1,344 @@ +# Track B B0.2: landscape + +**Status: synthesis of five landscape lanes, 2026-10-23, revised +2026-10-24 for public HRS. It computes nothing and freezes nothing.** It feeds B0.2's structural-audit +protocol, which is frozen before any count, and the questions for Max. + +Five read-only lanes ran on 2026-10-21 and 2026-10-22, as set out in the +B0.2 plan (`docs/design/track_b_b0_2_plan.md`, section 3, step 1): + +| Lane | Subject | Output | +|---|---|---| +| a1 | Repository and staged-data inventory | `lanes/a1-repo-inventory-out.md` | +| a2 | Microdata sources and access | `lanes/a2-microdata-access-out.md` | +| a3 | Public aggregate and administrative sources | `lanes/a3-aggregate-sources-out.md` | +| a4 | Exposure catalogue for each gate's candidate holdouts | `lanes/a4-exposure-catalog-out.md` | +| a5 | Per-gate requirements and J's joint record | `lanes/a5-gate-requirements-out.md` | + +The outputs are in +`microcosm-launch-evidence/dynasim-parity-20260909/trackb-b0-2-20261021/`. +No lane read survey microdata, computed a count or ran a script. Where a +lane could not open a source page, it marked the fact **UNVERIFIED**, and +this synthesis keeps that label. + +## 1. What each gate can draw on now + +**Staged and readable on this machine** (a1, section 1): +- PSID family files 1968-2023, the cross-year individual file, the + marriage history, childbirth history and relationship matrix; +- SSA's 2006 Earnings Public-Use File (EPUF), hash-pinned; +- committed SSA, Trustees, CBO, NCHS and DI ASR tables under + `data/external/`. + +**Not staged:** SIPP, CPS ASEC and CPS tenure files. **Absent:** any HRS +file or HRS loader, and any reader for SSA's Benefits and Earnings +Public-Use File (BEPUF), the Master Beneficiary Record or another +SSA-linked file. + +| Gate | What exists | What is missing | +|---|---|---| +| B3 | Head and spouse earnings at all ages from the family files; odd income years; EPUF through 2006; B2's and M6's cell and support helpers | A cell family for ages 62-69; other family members' earnings; an HRS loader if B3 uses HRS; evidence of an unused partition | +| B3L | The panel through income year 2022; boundary and origin already parameters | A 2006-origin domain reaching ages 62-69; everything B3 lacks | +| B4 | Marriage histories and transitions; candidate 16's port; roster-only widowhood; the DI engine; self-reported disability; deaths by year; forward support | A person-level administrative DI holdout; deaths separated from attrition; widowhood linked to the spouse's death; entrants | +| I's timing gate | Marriage months; DI award year; aggregate tables by month of entitlement; a SIPP job-month reader (data not staged) | Dated person-level death, award, claim and conversion events; a monthly-receipt loader; an event journal | +| B5-empirical | R1, R2 and R3 statutory modules; annual PSID Social Security amounts | Any withholding or deduction observation; RET-countable earnings | +| B6a | A marginal claiming distribution; first observed receipt year; receipt history; years of coverage | Insured status; an insured, unclaimed person-month risk set with entitlement months | +| B6b | The FRA schedule and B6a's pieces | Identifying policy variation, and B6a's gaps | +| J | Self-reported benefit types; opening status; spouse links; the G layer; exposed aggregate SSA tables | Estimands and limits; a holdout independent of B2; a joint truth builder; administrative payment and recipient denominators | + +**External candidates** (a2, a3), all still to be chosen: + +| Source | Strongest use | Access | Status | +|---|---|---|---| +| HRS public files and the RAND HRS longitudinal file | B3 at ages 62-69; B4 deaths separated from attrition; self-reported claim and DI dates for I; joint states for J | Registration; use follows the project's data-access record | Available for the estimands it supplies (2026-10-24); pages refused automated fetches | +| HRS files linked to SSA records | Administrative earnings, insured status and entitlement dates for B3, B6a and I | Virtual-desktop enclave run by a named person; 6-8 week decision (search snippet); likely an institutional sponsor and IRB | UNVERIFIED | +| SIPP public-use panels | Monthly receipt, earnings and marital status | Public download | Variables verified through the Census API | +| SIPP Synthetic Beta, validated on the Gold Standard File | Insured status and claim timing for older cohorts, to 2014 | Application approved in about five business days; real results only through Census validation | Verified on census.gov | +| FSRDC files linking SIPP, CPS or ACS to SSA records | Everything, including possible deduction fields | Person with Special Sworn Status on site; 6-12 months; $15,000-20,000 a year per project | Verified on the SAP catalog and RDC pages | +| BEPUF 2020 | Structural and recipient-side sanity checks only | Public CC0 download | **Fully synthetic** (section 5): not observed data, so not a holdout for J, B5-empirical or I | +| Disability Analysis File | Monthly disability participation, with birth and death dates | "Restricted public" | Access UNVERIFIED | +| SSA Supplement, DI ASR, OACT series, Monthly Snapshot | Aggregate stocks, flows, awards, withholding (6.E) | Public | Section and table indexes verified; several sections already exposed (section 2) | +| NCHS life tables, mortality files and HMD | B4 mortality | Public; HMD needs registration | HMD and NCHS share vital-registration inputs, so they are not independent of each other | +| ACS and CPS marital and earnings tables | B4 state shares; B3 employment margins | Public | Metadata verified | + +BEPUF 2020 was the strongest public candidate for J. It is fully +synthetic (section 5), which leaves these gaps. + +**Gaps no verified public source closes:** +- the insured, unclaimed person-month risk set (B6a); +- observed annual deductions and withholding (B5-empirical); +- a complete joint holdout for J. + +Each of these needs either a restricted linked source or the design's +narrower fallback: +- B6a: a marginal claiming distribution, under `NO_RISK_DENOMINATOR`; +- B5: statutory conformance and sensitivities only; +- J: `JOINT_VALIDATION_FAIL` or a declared narrower scope. + +## 2. Exposure + +**Prior uses recorded by a4.** Each gate's candidate data has already +been used as follows. None of these can be an unused holdout (design +section 5.2). + +- **M6 candidates and floors:** + - PSID earnings reference years 2016 and 2018, anchored 2014; + - flows 2015-2019; + - diagnostics on earnings 2020 and 2022 and flows 2020 and 2021; + - cohorts 25-44 and 45-64. + + These bear on B3 (its 62-64 band), B3L, B4 and J. +- **The q\* and ρ\* selectors:** pseudo-boundaries 2006, 2008 and 2010, + scored on reference years b, b + 2 and b + 4 at ages 25-64. These bear + on B3L and J. +- **B0.1's protocol v2:** fitted at origin 2006 and projected to 2008 + and 2010, ages 25-64, with derived summaries published. This bears on + B3L's 62-64 band. +- **Marital selectors:** first marriage and remarriage at boundaries 2006, + 2008 and 2010. This bears on B4 and J. +- **M4 disability floors:** PSID waves through 2023, ages 20-66. +- **Mortality floors:** waves 1969-2023, ages 25-85+. +- **The DI-entitlement diagnostic:** validated against the DI ASR 2023. +- **The claiming reference:** fitted 1998-2019, evaluated 2020-2022. +- **EPUF and `gate_epuf_fill`:** earnings 1951-2006, ages 22-74, with DEV + and TEST scores published. +- **B2:** PSID income years 2012 and 2014, cohorts 25-44 and 45-64; gated + p90 and zero-earnings rate. +- **The SSA Supplement:** design participants opened 2023 §6.B, 2024 §4.B + and §6.E, and 2025 §6.E, including numeric tables (design lines + 290-297). +- **SSA tables committed in the repository:** a1 adds those not on the + design's list: + - Supplement 6.B for 2014, 2025 and 2026; + - the 2025 4A, 4B, 5A and 6A snapshots; + - 2008 4C; + - DI ASR tables for 2007, 2008 and 2023; + - 2024 Supplement captures staged under + `psid-data/legal-capture-staging/`. + +The held-out data registry (PR #560, +`docs/design/track_b_holdout_registry.json`) records the scored gates +among these. B0.2's mechanical exposure inventory completes it. + +**HRS exposure is UNVERIFIED.** No executed HRS exercise was found, but +issue and PR threads were not searched. + +**Accidental exposures during the lanes.** Each lane recorded them. No +value was copied into any output. +- a1 and a2: source files print microdata or codebook counts (for + example `deaths.py`'s codebook tallies and `epuf.py`'s row counts). The + lanes read them and copied nothing. +- a3: four web pages showed numbers before the lane stopped reading: + - the Monthly Statistical Snapshot for September 2026; + - the DI ASR 2025 highlights; + - an OACT chart's alternative text; + - an NSFG methods page. + + A numeric erratum also appeared on the 2024 Supplement contents page, + and some search snippets showed outcomes. +- a4: a content search with no exclusions (`git grep -il HRS`) read the + restricted B2 outcome files' bytes. It displayed file names only. + Masking also failed on several files, showing outcome counts or figures: + - `docs/amendments/gate_epuf_fill_registration_proposal.md`; + - `docs/design/di_entitlement.md`; + - comments in `harness/m6_cells.py`; + - assertions in `scripts/build_m6_holdout_floors_v4.py`. +- a5: excerpts of the B0.1 audit and addendum showed planning and power + figures, and `b2.py:229-235` showed inherited outcome text. + +These lanes are author-side landscape roles, not blinded reviewers, so +none can serve as J's blinded reviewer. B0.2's exposure record carries +each incident. The exposed web pages (the Snapshot for September 2026 +and the DI ASR 2025 highlights) are exposure for any gate that would use +them. + +**What the mechanical inventory must add** (a4, section "A B0.2 +mechanical inventory"): +- **A candidate registry per gate:** source, reference year, interview + wave, event period, age, partition, denominator, concept and estimand. +- **Exclusions first:** apply them before any content is read. +- **Pattern families:** B0.1's families, plus families for earnings, + marital events, disability, mortality, claiming, RET, dates and joint + states. +- **Classification of each artifact,** separately by evidence content + and by role. Content is one of names or rules, observed values, + projected values, derived values, transient computation, or + unverified. Role is one of training, selection, floor, development, + diagnostic, regression or holdout. +- **Integers and code in scope:** cover hard-coded counts and code, not + only decimals. +- **An off-repository register** supplied by outcome-aware holders, for + issue and PR threads and external directories. + +## 3. Requirements per gate + +a5 sets out, for each gate, the ten section 5.2 elements and the power +procedure. For each element it gives what the design fixes, what B0.1's +rulings carry over, and the open options with a recommended default. +Its summary table marks almost every element of every gate "open: needs +Max" or "open: needs counts". Only exposure history is "carried over". + +Its main recommendations: +- **A common power procedure,** for Max's approval: prospective + whole-gate equivalence power. The operating characteristic is the + probability that a faithful candidate passes the entire registered + conjunction, evaluated over a frozen grid of nuisance assumptions, with + a conservative dependence treatment and finite-design and estimation + uncertainty included. +- **Permissible structural counts and forbidden outcome disclosures,** + per gate. a5's table "What the structural-audit protocol should allow + and forbid" becomes the protocol's core. Missingness and availability + flags can reveal outcomes (a "date available" flag can identify an + event), so the protocol must test that no permitted output reconstructs + a target truth cell. B0.1's death-disposition counts were structural + for B2 but would reveal part of B4's mortality surface. +- **J:** six candidate joint estimands (J1-J6): + - J1, a common-date earnings, claiming and DI table; + - J2, the earnings-claiming association by DI history; + - J3, joint transition pathways; + - J4, recipients and payment within joint states; + - J5, survivor-path cumulative incidence; + - J6, post-bereavement payment persistence. + + Each has limit options drawn from the design's styles (2-3 percentage + points for probabilities, 5-10% for amounts). a5 also states what + blinded ratification must certify. +- **B2's medians:** ask Max whether to assign a separately named + cohort-median component to B3, with matching regression cells in B3L, + or to leave medians unadmitted. The current p50 is a weighted + positive-earner median. No gate holds ages 25-61 earnings to a fixed + limit (`b2.py:215-216`). +- **The single §5.3 feasibility review for B3 and B3L:** its inputs and + procedure are set out (a5, final section). It uses B0.1 audit section + 11.3's figures only through a cleared review process. + +**H's start roster** (a1, section 4). No H specification exists. Three +rosters are candidates: +- B2's 2010 head-and-spouse domain, defined in code; +- the full 2011 anchor roster; +- Track A's 2011 closed cohort (`cohorts/psid2010.py`). + +Each changes B4's coverage declaration. The choice is a question for Max +or for H's design, and the count runs under B0.2's protocol. + +## 4. Pricing of the design's still-unpriced items + +These are estimates in engineer-days (d) of reviewed output, or calendar +time, with their basis in the cited lane: + +| Item (design §3.3) | Access | Loader and gate work | Source | +|---|---|---|---| +| HRS loader and access, public | About one week of registration; use follows the project's data-access record | Loader 4-8 d; B0.2 adds 1-2 d to freeze HRS elements per gate | a1 4-7 d, a2 5-8 d | +| HRS linked to SSA records | 2-4 months, about 6 if a sponsor is needed; 1-3 weeks of disclosure review per output | 6-10 d by a person in the enclave | a2 | +| Linked withholding data (B5-empirical) | Public: none, if BEPUF 2020 carries a deduction field (UNVERIFIED). Otherwise HRS-SSA (2-4 months) or an FSRDC (6-12 months, $15,000-20,000 a year) | 1 d documentation check; then 8-12 d to rebuild annual deductions; 4-6 d to freeze and run | a2 | +| I's timing gate: data, loaders, gate | Public HRS and SIPP: about one week; restricted fallback as above | SIPP extension 3-5 d; BEPUF 3-4 d; freezing estimands, limits and power 3-5 d; running the gate 4-6 d. About 10-20 d beyond B0.2's own 5-7 d | a1, a2 | +| Entrant initialization | Public ACS, CPS and EPUF | **Cited from the open-population lane: OP8, 25-39 d; 34-52 d with the demographic packets** (draft, PR #557 at `2acd0bed`, pending its review) | `inputs/entrant-initialization.md` | +| B6b access | Public HRS about one week; restricted 2-4 months; FSRDC 6-12 months | Shared with the HRS rows | a2 | +| 2032 and 2050 horizons | — | Not re-priced here. Design B priced its B8 at 40-70 d, for scale only (design line 203). The open-population lane prices its economic horizon separately (OP10b, OP11b; draft) | design; #557 | + +The insured-status rule that B6a, entrant initialization and OP12 need is +on the rules lane under d1802. a1's 2-3 d estimate for it is for scale +only. + +## 5. What must be verified before the protocol relies on it + +These are facts the lanes could not open. Verify them in a browser, +which works where automated fetches are refused, and open no numeric +table: +1. **BEPUF 2020: settled, 2026-10-23.** SSA's landing page + (https://www.ssa.gov/policy/docs/microdata/bepuf-2020/index.html, opened + in a browser; it showed no outcome figures) describes the file as "fully + synthetic microdata records statistically representing a self-weighted + 10 percent sample of the current adult OASDI beneficiary population as + of December 2020". It says each record's earnings history and benefit + credited are related "by statistical modeling rather than by applying + Social Security program rules", and cautions against using them + together for individual-level analysis. A synthetic file is not + observed data, so it cannot serve as an observed holdout for J, + B5-empirical or I's timing gate, whatever its fields. The user guide + PDF did not render as text in the browser, and its field list is + unread; nothing above depends on it. +2. **HRS.** Public HRS is settled (2026-10-24): it counts as available + for the estimands it supplies, and its use follows the project's + data-access record. Restricted HRS-SSA access, its eligibility and + lead time remain to be verified. +3. **The Disability Analysis File's access route.** +4. **Two staged files that may be restricted** (a1, section 7). Do not + open them until someone rules: + - `psid-data/validation-sources/urban-favreault2007-311436.pdf`; + - the `ssa-mint-*` captures. + +## 6. What the protocol can count, and what waits for Max + +PSID is the only staged source whose structure B0.2 can count now. So +protocol version 1 covers structural counts on the staged PSID files for: +- B3: support at ages 62-69 by wave, households and design units; +- B3L: the 2006-origin roster and later availability; +- B4: the opening roster, linkage coverage and design fields; +- H's candidate rosters; +- J: PSID-side linkage coverage outside B2's evaluation households. + +It applies a5's forbidden-disclosure lists. Gates with no staged source +(I, B5-empirical, B6a, B6b) get a source decision first. Their protocol +sections follow once Max chooses sources and the access questions above +are answered. + +Questions for Max, gathered from a2 and a5, go to him through +`cos add-decision` when the records are written. They include: +- the common power procedure; +- each gate's source; +- whether to pursue restricted HRS-SSA or FSRDC access; +- H's roster; +- B2's medians; +- J's estimands and limits; +- the percentage-error conventions. + +## 7. A decision needed now: restricted linked data + +Design Q4 (line 606, approved in d515) sets the order: evaluate public +HRS and SSA sources first, and request restricted linked data "only if +B0 shows that public sources cannot supply the risk denominator, the +timing observations or the withholding observations". + +The landscape shows that condition is met for three of those needs: +- **No verified public source supplies B6a's risk denominator:** an + insured, unclaimed person-month risk set with entitlement dates. + Public HRS does not observe insured status (a2, section 2.1). +- **No verified public source supplies observed withholding or annual + deductions for B5-empirical.** Supplement table 6.E4 is a December + status diagnostic (design line 224), and BEPUF 2020 is synthetic + (section 5). +- **No verified public source supplies J's joint holdout,** with + observed payment and recipient denominators. + +Restricted access is slow: +- HRS linked to SSA records: about 2-4 months, through a named + person's virtual desktop in an enclave, probably with an institutional + sponsor (UNVERIFIED); +- an FSRDC: 6-12 months and $15,000-20,000 a year per project, with a + person holding Special Sworn Status working on site (verified on the + RDC pages). + +These lead times would fall on the path to B6a and J, and so to B7. +On both routes a person does the analysis inside the enclave or data +center. + +The options: +1. **Apply for HRS-SSA access now,** in parallel with B0.2. It supplies + insured status, entitlement dates and administrative earnings. It + leaves B5's withholding UNVERIFIED (it depends on the benefit file's + detail). +2. **Apply for an FSRDC project now.** This is the only verified route to + Master Beneficiary Record payment data. Whether that data carries + work-deduction fields is UNVERIFIED. +3. **Both.** +4. **Neither, and accept the design's fallbacks:** + - B5 publishes statutory conformance and sensitivities only; + - B6a keeps a marginal claiming distribution (`NO_RISK_DENOMINATOR`); + - J is narrowed to what PSID or public HRS can observe, or stays + blocked, and B7 is limited accordingly. + +Public HRS is needed under every option. It now counts as available +for the estimands it supplies (2026-10-24), and its use follows the +project's data-access record. Which estimands those are is fixed in +the J and B6a records, before any HRS file is read. diff --git a/docs/design/track_b_b0_2_plan.md b/docs/design/track_b_b0_2_plan.md new file mode 100644 index 00000000..2e1505cd --- /dev/null +++ b/docs/design/track_b_b0_2_plan.md @@ -0,0 +1,163 @@ +# Track B B0.2: plan + +**Status: plan, 2026-10-21. This is not an audit record, and no gate can +register on it.** Max ruled in d1802 (2026-10-21) that B0.2 starts now, +on the Track B lane, after the B2 successor memo. This file sets out what +B0.2 must deliver, the order of work and the rules that keep it apart +from the successor design. It changes as the work goes; the audit +records that follow it are the frozen artifacts. + +"The design" means +`microcosm-launch-evidence/dynasim-parity-20260909/trackb-design-20260928.md` +(revision 3), which sits outside this repository. Line numbers refer to +that file. + +## 1. What B0.2 must deliver + +- **One frozen record per gate** (design line 288): B3, B3L, B4, I's + observational timing gate, B5-empirical RET, B6a, B6b and J. Each + record lists the ten §5.2 elements, read as B0.1 read them + (`docs/design/track_b_b0_1_audit.md`, section 2): source release, + variables, population, exposure history, holdout partition, critical + cells, numerical tolerances, uncertainty treatment, pass conjunction, + and reference dates kept separate from interview and publication + availability. Each record also names its power procedure. A gate + cannot register until its record is frozen. +- **A structural-audit protocol frozen before any count or power + summary** (line 286). The record keeps reference dates apart from + availability. Outcomes that were already inspected stay regression + evidence, and "later" or "external" does not by itself establish an + unused holdout. +- **A blinded review of prospective feasibility and power** for the + §5.3 limits (line 120). It may find a limit infeasible before any + candidate result, which leads to prospective rescoping; it may never + loosen a limit after candidate results (line 308). +- **J's joint estimands and numerical limits**, frozen in B0.2 and + ratified by a blinded reviewer before B3 registers (lines 230, 320, + 365-368; Q10, line 615). J's holdout must be independent of B2's + evaluation data. +- **Exposure.** B2's result is recorded as exposed before J's holdout is + chosen (lines 297, 368). +- **The held-out data registry.** `docs/design/track_b_holdout_registry.json` + (PR #560, on its own so that every lane can cite it on master) is the + single registry of held-out years, editions, tables and partitions for + every Track B gate and every open-population gate (coordinator, + 2026-10-21). J's candidates are registered there first; the + open-population lane's OP0.2 registers its picks for OP12 before it + freezes them; no unit is held out by two gates silently. B0.2's + mechanical exposure inventory, `docs/design/track_b_b0_2_exposure_inventory.json`, + completes it. That inventory must cover every gate with a committed + scored run, not only the five the registry seeds. #560's round-4 + review found four more with committed scored outputs: `gate_1`, + `gate_2`, `gate_2b` and `gate_2c`. Each becomes an authorized + historical import in the registry module, or is recorded as holding + out nothing that a Track B or open-population gate could use. +- **Pricing of the still-unpriced items** (§3.3, line 198; Q13, line + 618): data access, loaders and gate work for I's observational timing + gate; the HRS loader and access; linked-withholding data access and + loaders for empirical RET; entrant initialization; B6b's access time; + and the 2032 and 2050 horizon extensions. Q4 (line 606) sets the order + for data: public HRS and SSA sources are evaluated first, and + restricted linked data are requested only if public sources cannot + supply the risk denominator, the timing observations or the + withholding observations. +- **Projected years past 2030.** The open-population design, approved + as recommended in d1925 (2026-10-23; PR #557, its question 5), sets + this principle: every projected year past 2030 is published as + conditional on named assumptions, with drift diagnostics, and never + as a validated forecast, and the DYNASIM comparisons at 2050, 2065 + and 2070 are registered, report-only and blind. It bears on B7's + choice of target years and on J's record. +- **B0.1's handoffs** (`docs/design/track_b_b0_1_addendum.md`, "To + B0.2"): + - the marital selectors' pseudo-boundaries 2008 and 2010 bear on B4; + - B3L's 2006 boundary is a q\* and ρ\* pseudo-boundary whose floors + and outcomes are in the ledgers, and protocol v2 projected candidate + 3's law from 2006 to 2008 and 2010 at ages 25-64, which includes + B3L's 62-64 band; + - §5.3 feasibility is reviewed once, for B3 and B3L, with B0.1's + section 11.3 figures as inputs; + - H's spec names and counts its 2010 start roster, and B4's coverage + declaration cites it; + - the addendum's design rule (its section 16, item 10: household + resampling stands only if every cell's design-ratio 95th percentile + is at most 1.10) did not hold at the 2006 pseudo-origin, so the + choice between inflating standard errors and resampling design + clusters arises again for every later gate. +- **B2's medians.** Under d1606 option C1, B2 gates the 90th percentile + and the zero-earnings rate by cohort; its two median cells are + report-only (`src/populace_dynamics/track_b/b2.py`, `GATE_SURFACE` and + `HEADLINE_SCOPE`). B0.2 records which later gate, if any, covers the + medians, and states that nothing downstream may rely on B2 for them. + +## 2. Separation from the successor design + +Under d1659 a B2 successor is designed on evidence independent of B2's +outcome. Its design inputs live in +`microcosm-launch-evidence/dynasim-parity-20260909/trackb-b2-successor-20261018/`. +B0.2 and that lane must not see each other's evidence. + +- No B0.2 lane or reviewer reads the successor folder, or anything + derived from it. B0.2's own working files live in + `microcosm-launch-evidence/dynasim-parity-20260909/trackb-b0-2-20261021/`, + and its lanes run in a standalone clone with the design and + `RESTRICTED-FILES.md` copied in, and nothing else from the evidence + directory. +- The successor lane must not read B0.2's counts, planning values or + records, or B0.2's working folder. Before any B0.2 file holding + real-data counts reaches master, B0.2 adds an exclusion inventory in + the format `scripts/track_b_b0_1_review_root.py` reads + (`q6_exclusions`), which successor-lane review roots name. +- B2's outcome is not restricted for B0.2. The 2026-10-18 entry in + `RESTRICTED-FILES.md` restricts B2's outcome files for roles building + or proposing a successor blind, and B0.2 must record that exposure. + B0.2's blinded roles still get roots built by the review-root + builder, which always drops the standing B2-outcome record + (`docs/design/track_b_b2_outcome_exclusions.json`); they read B2's + exposure from B0.2's own record. + +## 3. Order of work + +1. **Landscape (no counts).** Separate lanes, none of which sees + another's output before synthesis: + - a repository and staged-data inventory: loaders, staged sources + and what each gate needs against what exists, from listings and + code only, with no survey values read; + - external sources and access: public and restricted HRS, SIPP + public-use and SSA-linked files, SSA public-use files, sources of + withholding and event-timing observations, and inputs for entrant + initialization, each with access steps, lead time, cost and the + gates it could serve; + - a mechanical exposure inventory for the candidate holdouts of each + gate, extending B0.1's inventory approach; + - a per-gate requirements table drawn from the design text. +2. **Protocol.** One B0.2 structural-audit protocol, reviewed by GPT-6.1 + Sol (`--tier hard`), then committed and hash-pinned before any count + runs, as B0.1's section 16 was. +3. **Counts and power.** One-shot count scripts, run once from a clean + worktree at the protocol commit, writing exclusive outputs. Tests + include value-invariance tests in the style of + `test_counts_are_invariant_to_valid_earnings_values`, Hypothesis + property tests and mutation checks. +4. **Records.** The per-gate records, the pricing table and questions + for Max with recommended defaults, queued with `cos add-decision`. +5. **Reviews.** A Sol review of each record; the blinded + feasibility-and-power review; the blinded ratification of J's record + before B3 registers. + +The work may split into several pull requests (plan and protocol, +counts, records). This draft pull request opens with the plan. + +## 4. Rules + +- Design §5.1: no edits to `engine/loop.py`, `engine/steps.py`, + `gates.yaml` or committed `runs/*.json`. +- B0.2 fits no model, runs no projection and computes no candidate + outcome. What it may compute from real data is fixed by its protocol. +- Local runs use at most four BLAS and OpenMP threads with + `OMP_WAIT_POLICY=PASSIVE`, and at most four test workers. +- Any change to `tests/tier_counts.json` waits for a merge slot from the + Microcosm dynamics coordinator. +- B0.2's 5-7 engineer-day estimate (line 120) predates the timing gate + and is not re-priced in the design (line 201). B0.2 reports its actual + effort beside that estimate. diff --git a/docs/design/track_b_b0_2_protocol.md b/docs/design/track_b_b0_2_protocol.md new file mode 100644 index 00000000..42236dd3 --- /dev/null +++ b/docs/design/track_b_b0_2_protocol.md @@ -0,0 +1,1043 @@ +# Track B B0.2: structural-audit protocol + +**Status: version 1, FROZEN, 2026-10-27. Block SHA-256 `6840f5fa41c897407c5896d4c3729ca5a5c0f1d166faa294e65e42309908ad23`. Run once, on 2026-10-29 from `e8a9fac0`: released.** + +Design section 5.2 (line 286) says B0's structural-audit protocol is +frozen before any count or power summary is computed. This file is that +protocol for B0.2. Version 1 covers only what the staged PSID can +answer. It is the first count step the landscape synthesis identifies +(`docs/design/track_b_b0_2_landscape.md`, section 6). + +A later version, recorded as such, will cover the other sources (HRS, +SIPP, SSA tables) once Max chooses them. + +The counting script `scripts/track_b_b0_2_counts.py` and its tests +arrived in `be7d4f42`. The unit test `tests/test_track_b_b0_2_counts.py` +pins the block's SHA-256, taken over the text from the begin marker +through the end marker. Adding the script did not freeze the protocol. +The freeze is a later commit that changes only the status line above +to FROZEN and states the pinned SHA-256. It follows an independent +review of the block and the script that leaves no blocking finding. The +script then runs once, from a clean worktree at that commit. If the +review changes the block, the pin and the script change with it before +the freeze. + +Review round 1 (Sol, at `ec9eb486`) asked for changes, and the block was +revised before any freeze: +- **Ages:** a known-age definition, so a missing age (999) is in no band + and not in the Track A proxy. +- **Disclosure:** coarsening and an integer-programming audit, which + replace subtraction-based complementary suppression, after two + executed recoveries of small cells. +- **One run:** a frozen-status check and an attempt record. + +Review round 2 (Sol, at `0644b717`) asked for changes again, and the +block was revised before any freeze: +- **Audit metadata:** the committed file had reported how many targets + were audited, which an executed fixture used to narrow a remainder to + four sizes. The disclosure block now holds only the base, the rules + and the status. No program the audit runs depends on a true size, so + a reader can rerun it, and every target it finds narrow is treated as + disclosed. +- **Intervals:** a published multiple stands for max(B, m − B/2) to + m + B/2 − 1, matching the coarsening rule. +- **Writes:** both files are written to temporary names, flushed and + linked into place, so a failure leaves no partial file. +- **Nonblocking findings, also answered:** relations across unit types + are part of the audit's model; a single fixed base replaces the + ladder; a target is wide on two verified points, without a proven + optimum; and tests now cover a fractional age of 62.5, negative + inputs to coarsening, the solver's exact-optimum setting and the + attempt record's flush. + +Before round 3, three sentences of the block were corrected. They had +said a wide target keeps at least five possible sizes; the rule, and the +tests, guarantee a range of width at least 4. The evidence for the +revision (an enumeration oracle, searches, an invented-panel calibration +and the mutation runner) is in `docs/design/track_b_b0_2_evidence/`. + +Review round 3 (Sol, at `2d0ed992`) asked for changes, and the block was +revised before any freeze: +- **B0.1's counts:** B0.1 had already published exact counts of the same + persons, which the audit did not model. Two executed tables used them: + - with 97 invented B2 heads, B0.1's exact count of those aged 45-64, + against this release's 45-61 band published as 100 (95 to 104), left + the 62-64 band one or two; + - a five-head table, both bands under 10, left the 62-64 band one to + four. + + B0.1's sets are now rebuilt in memory by its own rules and enter the + audit at their exact counts, after a rerun of B0.1's script on the same + inputs must reproduce its record. This required an exception to the + rule against counting dispositions: B0.1's disposition sets are among + them. Nothing derived from them beyond B0.1's own counts is written. +- **An emptied pool:** disclosures could rule out every pool point, + leaving nothing to classify against. An empty pool now makes its + target narrow, the seed programs run again under the disclosures, and + the release is withheld if they find nothing. +- **The program limit** is now 300 seconds, up from 60. Round 3's rerun + of the 800-household calibration withheld on a loaded host when six of + the nine seed programs found no point within 60 seconds. A limit sets + no window and sees no size, so it changes only how often a slow + machine withholds a release. +- **Nonblocking:** `write_new`'s docstring now says that a failure after + the link leaves the complete file; and tests now kill the two focused + mutants round 3 found alive (a shifted seed, an omitted directory + flush). + +After round 3, the coordinator ruled that B0.1's sets may constrain the +audit on conditions, which the block now states (Disclosure rule, +"B0.1's sets: the conditions on their use"): a rerun gate, a write +boundary over every output path, a decision record, a stated limit, an +invariant test and no precedent. Meeting them changed the script before +any freeze: +- **The local copy** now holds only this release's counts and the + audit's final decisions. It had also held each target's proof, + programs and range, the pool's size, the relation count and the times, + which depend on atoms that B0.1's sets refine, and the rounds, which + add resolution to the decisions. +- **Provenance** goes into the committed file, so it holds only the + requested shape, fixed before any count, and hashes of inputs and + code. +- **Failures and warnings** print no value. A library's message, which + can quote a size, is dropped. A warning prints nothing, since whether + one fires, and where, can depend on the data. The solver prints + nothing. +- **The rerun gate** checks each rebuilt set against its count before the + audit, where it had been checked only after registration. +- **Targets.** Two of this release's sets always make a target, and + their differences come first. Before, a difference of two of this + release's sets that both coincided with B0.1 sets was skipped, and a + difference involving a B0.1 set that held the same units could be + reached first, so which targets were this release's own depended on + B0.1's sets. + +The coordinator then ruled that a stop counts as a decision, a withhold +of the version, and narrowed its report: +- **A stop withholds the version.** The attempt record already refused + a rerun; the block now says that the stop is a decision, and that the + next attempt is a new version, ruled on before it runs, that treats the + stop as public. +- **The report follows the phase.** A failure while reading the inputs + or writing the files still names its type and the script's line. One + in the release, where dispositions and atom sizes enter, names the + phase alone. The script's own raises keep their fixed messages in + every phase. +- **Nothing printed inside a phase is seen.** File descriptors 1 and 2 + point at the null device inside each phase, and every stream is + flushed before they are restored, so output from compiled code is + covered too. +- **The stated limit** now names the run's total time as a channel, and + says that no time is written or printed. +- **Operations.** The one run is detached from the session that starts + it, with no wall limit, since the number of programs is bounded. Since + any stop spends the version, and each program's limit is wall-clock + time, the coordinator set three conditions in its place, and the + script enforces the first two before the attempt record: + - it holds off sleep with `caffeinate` on its own process; + - it runs at nice 0, and starts only when the five-minute load is below + the CPU count; + - an expected run time, extrapolated from the calibrations, goes to the + coordinator before the freeze. + + The run prints nothing until it ends; its liveness is checked by its + process ID. + +Three tests failed when they first ran, and each was fixed in the +tests alone: +- the property-based relabeling test built its folder behind a symbolic + link on macOS, which `main()` resolves; +- the exhaustive readers compared a target that is both wide and + irrelevant by name, though the script may prove either; +- the test that records the solver's options kept the dicts it was + given, from which SciPy's `milp` removes `disp`; it now keeps copies. + +The relabeling test, and its fixed companion, now include panels that +release as well as panels that withhold. + +Review round 4 (Opus, at `60d77cff`) approved the block and the script. +It found four test gaps (T1–T4), four script gaps (S1–S4) and three +points for the block (B1–B3), and the coordinator ruled that all eleven +go in before the freeze: +- **Tests (T1–T4):** + - `main()` runs under an audit hook that refuses any write outside its + three outputs, and any new process; + - a property test checks that the local copy is the same whatever the + rounds; + - dropping B0.1's household sets now fails a test; + - the relabeling property draws its swaps from heads of different ages + and checks that the atoms change. +- **The decision record (S1):** a target the round that ended the audit + did not reach is `unreached`, whichever round that was, and each class + names this release's sets that hold it. +- **The run's conditions (S2):** + - the sleep assertions are held in-process through IOKit, in place of + `caffeinate`, whose death the script would not have seen; + - AC power, the Darwin background policy and the QoS class are checked + with niceness and the load. A measurement after the round added the + QoS class: under `taskpolicy -c background` a CPU loop ran about a + twelfth as fast, with niceness and Darwin priority both 0. The class + must be unspecified, or the default or above; + - every condition is checked again before every program, and a failure + is waited out between programs for up to 24 hours before the run + stops. +- **Separation (S3):** the evidence folder's twelve files are on the + exclusion record, and the name rule and the exclusions both read full + paths, which a test with same-basename decoys checks. +- **Identifiers (S4):** a present unit without its person or interview + number, or its design variables, stops the run while reading. +- **The block (B1–B3):** + - the status of a withheld release is listed among the audit's limits; + - condition 4 names the script's own checks as part of the stop + channel; + - the operations rule says that running without a wall limit rests on + the calibrations, not on the bound. + +Review round 5 (Sol, at `c6aec979`) stopped before its report, but its +draft found two gaps in round 4's items, both fixed since: +- **The sleep assertions (S2):** the check counted an assertion that + still existed as held, though IOKit lets its owner set it to the off + level, where it no longer prevents sleep. It now reads each one's + level, and one at the off level is given back and taken again. +- **The test confinement (T1):** it let a write into an existing + bytecode cache folder pass, and did not see a database, the system + log, a change to a file's metadata, or a socket until it connected or + sent to an address. It now refuses each, and + bytecode writing is off while it holds. A write that native code makes + through its own descriptor raises no audit event, so the static check + also refuses the modules that could make one. + +Two sentences in the block were corrected: the identifier check can fire +on the real files, since the person-wave reader passes the interview +number through as read; and round 4's two status tables are described +separately. + +Review round 5's second attempt (Sol, at `9b9d53f5`) found the second +table's description still wrong: the result for 0 to 2 heads is the +reader's rerun on one table's fixed atoms, not the natural tables', and +the block now says so. It also found two mutations no test caught, both +now caught: a properties dictionary the assertion check copies and does +not give back, and a design stratum the identifier check does not +require. The test confinement also refuses name lookups. + +The invented-panel calibrations at `e79f41b5` then withheld at 800 and +3,000 households, each on one class of persons of true size 1 that the +audit left 0 to 3, and released with B0.1's sets left out +(`docs/design/track_b_b0_2_evidence/calibration.txt` and +`singleton.txt`). Max ruled on 2026-10-26 that a target is exempt when +this release's counts narrow it no further than B0.1's published counts +already do, and the block was revised before any freeze (Disclosure +rule, "Exempt targets"): +- **The test.** For each narrow target with a proven range, a class or + a difference alike, two more programs find its least and greatest + size under B0.1's published counts alone. They are set from the + publications, like every other program. If they prove the same + range, the target is exempt: it neither withholds the release nor is + disclosed. A target holding an atom no B0.1 set counts is never + exempt, and anything unproven fails closed. +- **Later rounds** test an exempt target again, so a disclosure that + narrows it further ends its exemption. +- **Tests:** + - the exhaustive reader with earlier counts also enumerates B0.1's + counts alone, and the script's exemptions must match it; + - the exemption programs are the same for every table with the same + publications, and each fail-closed case has its own test; + - through `main()`, a panel whose small classes, and a small + difference, B0.1 already fixes releases, and switching off any one + of those exemptions withholds on that target alone. That panel had + been the relabeling test's withholding case, which now uses one this + release narrows, and the test of a small B0.1 cell now names the + target that still withholds it. +- **The program bound** grows to 9(T + 2) + 7T(T + 1)/2. + +The mutation record and the calibrations run again on the final source. + + +## Structural-audit protocol, version 1 (frozen before any count) + +### Scope + +Version 1 counts the structure of the staged PSID for four purposes: + +1. **H's candidate start rosters.** B4's coverage declaration cites H's + roster (B0.1 addendum, "To B0.2"). +2. **B3's support at ages 62-69.** These are B3's bands, design line 127. +3. **B3L's 2006-origin roster and its later support.** B3L is the + 2006→2022 backtest, design line 128. +4. **The PSID persons and households outside B2's evaluation units.** + J needs a holdout independent of B2's evaluation data (design lines + 365-368). + +It computes no power quantity. The power procedure waits for Max's +ruling on it. + +### What this audit may and may not compute + +**It may compute:** +- counts of persons, of households (interview numbers) and of survey + design strata and clusters; +- counts of valid head and spouse labor-income rows, and of same-person + valid-row pairs at fixed lags; +- Kish and cluster-worst effective sizes from weights; +- SHA-256 hashes of the files it opens. + +**It may not:** +- read or compute any labor-income level, sign, zero or positive + indicator, quantile or change; +- read any marital, marriage-history, death, disability, + employment-status, Social Security, wealth or other income variable; +- count dispositions that separate death from attrition, or any + transition. The one exception is B0.1's published sets, its disposition + sets among them. They are rebuilt in memory, by B0.1's own rules, only + to constrain the audit, under the conditions the Disclosure rule sets + out ("B0.1's sets: the conditions on their use"). Nothing derived from + them beyond B0.1's own counts and the audit's decisions is written or + printed anywhere; +- compute any planning value, power probability, model fit, projection, + floor, gap or candidate outcome. + +These rules apply the forbidden-disclosure list in landscape lane a5 +("What the structural-audit protocol should allow and forbid") to the +PSID. In particular, for B3 and B3L no earnings sign, positive-pair +count, participation, transition, crossing, quantile, cumulative-earnings +or persistence value is computed. For B4, no marital state, transition, +DI status, death, survival or widowhood frequency is computed. For J, no +joint state, recipient or payment value is computed. + +### Inputs + +1. **The staged PSID directory,** resolved by the repository's own rule + (`populace_dynamics.data.psid`). +2. **The cross-year individual file `IND2023ER`,** read through + `panels.ind_person_period` with exactly the concept labels B0.1's + counting script uses for age, sequence number, relationship to the + reference person, individual weight and interview number + (`scripts/track_b_b0_1_counts.py`, `_individual_variables`). It covers + collection waves 1999 to 2023, every second year, plus 2007, 2009 and + 2011 where those are not already included. +3. **The 2011 anchor-wave variables,** read through + `cohorts.psid2010.read_anchor_wave(anchor_wave=2011)`: interview, + sequence, relationship, reported age and the 2011 cross-sectional + weight. Its `reported_birth_year` column is dropped as soon as it is + read. +4. **The survey design variables,** read through + `cohorts.age67.read_design_variables`: sampling error stratum + (ER31996) and cluster (ER31997). +5. **The family files for collection waves 1999 to 2023,** read through + `family.read_family_labor` with its label verification, then reduced + at once by B0.1's `validity_frame` + (`scripts/track_b_b0_1_counts.py`), which is imported unchanged. + +6. **B0.1's committed counts,** `docs/design/track_b_b0_1_counts.json`. + They are public and exact, so they constrain the audit, and they + decide which narrow targets are exempt (Disclosure rule). Before any + of B0.1's sets enters the audit, the script reruns B0.1's + `structural_counts` (`scripts/track_b_b0_1_counts.py`) on its + own inputs and stops unless the result equals the committed counts + exactly and each rebuilt set's size is the count B0.1 published for + it. Each ledger count B0.1 copied must equal the B0.1 count it + repeats. + +No other file or variable is opened. The script records the label of +every variable it requests, and a test pins that list as an allowlist. + +### Reduction at read time + +Each family file's labor-income levels are replaced by one flag per +role, "below the PSID missing sentinel", using B0.1's `validity_frame`. +The accuracy codes are replaced by "positive". No later step sees a +level. + +A unit test replaces every valid level with an arbitrary valid level and +requires every output to stay byte-identical. A second unit test adds +arbitrary extra columns of a forbidden kind to the input frames (marital +status, death year, Social Security amount) and requires every output to +stay byte-identical. + +Every unit a published set counts must have its identifiers. Each present +row (sequence number 1 to 20 and a positive weight) needs its person and +interview number, and each present person a design stratum and cluster. +Without one, a person would be counted but sit in no household or design +cluster, so an effective size could exceed the count it bounds. A +missing identifier stops the run while reading, before any set is built. +Whether it fires on the real files is not known before the run: +`panels.ind_person_period` builds the person number as an integer, but +passes each wave's interview number through as `pandas.read_fwf` reads +it, and a blank field reads as missing. + +### Definitions + +- **Collection wave and reference year.** Collection wave *w* collects + reference year *w* − 1. Ages are collection-wave ages, as in B2 + (`src/populace_dynamics/track_b/b2.py`, `cohort`). The B3 and B3L + records may choose another age convention; version 1 counts at + collection-wave age and says so. +- **Valid row.** At wave *w*, a person present (sequence 1-20) who is the + head or reference person, or the wife, spouse or partner, under that + wave's relationship codes, with a positive weight at *w*, and whose + role's labor income is below the sentinel. This is B0.1's + `earnings_rows` rule, imported unchanged. +- **Known age.** An age is known when it is an integer from 1 to 125, + the range the repository's readers accept + (`cohorts.age67._MAX_AGE_CODE`, which treats larger values, such as + 999, as missing). Any other value, including 0 and 999, is an unknown + age. A person of unknown age is in no age band and in no age-defined + roster. +- **Age bands,** on known ages only. + - Older bands: 62-64, 65-66, 67-69 (B3's bands). + - Context bands: 25-44, 45-61. + - Roster bands: 1-24, 25-44, 45-61, 62-64, 65-66, 67-69, 70-79, 80 and + over. +- **Rosters at the 2011 wave** (H's candidates): + - `R_FULL`: persons present (sequence 1-20) with a positive individual + weight at 2011. This is B0.1's full anchor. + - `R_B2`: B2's domain, `R_FULL` persons with a valid 2010-reference row + (`b2.domain_person_ids`). The script asserts that its size equals + B0.1's committed count (`docs/design/track_b_b0_1_counts.json`). + B0.1's counts also constrain the audit (Inputs, item 6). + - `R_TA_PROXY`: persons present at 2011 with a positive 2011 + cross-sectional weight and a known 2011 age of 30 or over. + - It is a proxy for Track A's closed cohort (persons born in 1980 or + earlier, `cohorts/psid2010.py`). + - Track A's own birth-year law reads marriage histories and earnings, + so version 1 does not reproduce it. The H record cites Track A's + own record for the exact cohort. +- **Roster counts.** For each roster, overall, by roster band at 2011, + and for unknown age at 2011, so that the bands and unknown age + partition the roster: + - persons; + - households (2011 interview numbers); + - design strata and clusters; + - Kish effective size; + - cluster-worst effective size, with households as clusters, and again + with design clusters as clusters. + + Weights are the roster's own weight: individual weight for `R_FULL` + and `R_B2`, the cross-sectional weight for `R_TA_PROXY`. +- **B3 support.** For each wave *w* from 1999 to 2023 and each older + band: + - valid rows, persons and households; + - design strata and clusters; + - Kish and cluster-worst effective sizes, using the wave's individual + weight; + - the number of those rows whose accuracy code is positive (a + data-quality count). + + Across waves: distinct persons per older band. +- **B3 lag pairs.** For lags of 2 and 4 years, and for each pair of waves + (*w*, *w* + lag) inside 1999-2023, count persons with a valid row at + both waves, by older band at the later wave. These are structural + support for transition and persistence estimands; no earnings value + enters. +- **B3L roster and later support.** + - `R2006_FULL`: persons present with a positive individual weight at + the 2007 wave. + - `D2006`: those `R2006_FULL` persons with a valid row at 2007 (income + year 2006). + - Roster counts for both, as above, by roster band at 2007. + - Then, for each later wave *w* from 2009 to 2023: the number of + `D2006` persons with a valid row at *w*, by band at collection-wave + age (context and older bands), and the weighted Kish size of those + rows using `D2006`'s 2007 weight. + - No disposition (death, move-out, nonresponse) is counted. +- **Outside B2's evaluation units.** + - B2's evaluation units are the `R_B2` persons and every 2011 household + containing one. + - Count the `R_FULL` persons outside those households, overall and by + roster band: persons, households, design strata and clusters, and + Kish and cluster-worst effective sizes. + - Separately, at waves 2013 and 2015, count the households (wave + interview numbers) containing at least one `R_B2` person. These + count households, not outcomes. +- **Effective sizes.** Kish size (Σw)² / Σw². Cluster-worst size + (Σw)² / Σ_c W_c², where W_c is cluster *c*'s total weight. These are + B0.1's `kish_n_eff` and `cluster_worst_n_eff`, imported unchanged. A + set with no positive weight reports 0. + +### Disclosure rule + +PSID outputs are aggregate statistics only, and no small cell is +committed, per the project's data-access record. Suppressing small +cells, and their complements by subtraction, is not enough: integer and +nonnegativity bounds, and remainders that are never published, can still +pin a small cell exactly (review round 1 of this protocol). Auditing the +published values alone is not enough either: anything else the committed +file says about the audit, including whether it passed, is evidence a +reader can use (review round 2). So the committed output is coarsened at +one fixed base, then audited against everything it discloses, by these +rules. + +- **Coarsening at base B = 10.** + - A count of 0 is written as 0. + - A count from 1 to B − 1 is written as the string `"