From cf6b511e154358592addece46b9f5301b7e6ccbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sat, 26 Sep 2026 22:48:35 +0200 Subject: [PATCH 1/5] build(deps): bump aw-core and aw-server-rust for the parity fixes aw-core 33c8528 -> 99cb7ac (#161, #167, #168, #169, #159) and aw-server-rust c3baa9c -> e7c5439 (#744, #748, #749, #750, #753). 433 known failures now pass, none are new. The remaining 532 are re-attributed with attribute.py from runs at the new pins: base, aw-core#165 alone, aw-core#166 alone, and both (with two open fixes, each leave-one-out run is the other fix alone). 81 are fixed by those PRs (CORE_165: 22, CORE_166: 59); the other 451 are the #1466 output shapes (SHAPE) and 1 ms timestamp resolution (PRECISION). --- aw-core | 2 +- aw-server-rust | 2 +- scripts/tests/query_parity/known_failures.txt | 553 ++---------------- 3 files changed, 62 insertions(+), 495 deletions(-) diff --git a/aw-core b/aw-core index 33c8528cf..99cb7aca7 160000 --- a/aw-core +++ b/aw-core @@ -1 +1 @@ -Subproject commit 33c8528cf0749b575c3ce0fb634e7d6e19b26ce4 +Subproject commit 99cb7aca7afcb17572e3afc6a2e42eb5827d5622 diff --git a/aw-server-rust b/aw-server-rust index c3baa9c3f..e7c54393f 160000 --- a/aw-server-rust +++ b/aw-server-rust @@ -1 +1 @@ -Subproject commit c3baa9c3f6661e52f2401836274b3c843f3e2614 +Subproject commit e7c54393f6d761c8feb83ea0bd1688fcfe6b3e18 diff --git a/scripts/tests/query_parity/known_failures.txt b/scripts/tests/query_parity/known_failures.txt index bf78c8168..8db7a33d9 100644 --- a/scripts/tests/query_parity/known_failures.txt +++ b/scripts/tests/query_parity/known_failures.txt @@ -1,116 +1,57 @@ adjacent-p0-chunk_events_by_key SHAPE adjacent-p0-flood CORE_165 -adjacent-p0-flood_pulsetime CORE_165,RUST_746 +adjacent-p0-flood_pulsetime CORE_165 adjacent-p0-merge_events_by_keys_app SHAPE adjacent-p0-merge_events_by_keys_missing SHAPE -adjacent-p0-nested_call_args CORE_164 adjacent-p0-tag_list_names SHAPE adjacent-p0-union_no_overlap_flooded CORE_165 containment-p0-chunk_events_by_key SHAPE -containment-p0-flood_pulsetime RUST_746 containment-p0-merge_events_by_keys_app SHAPE containment-p0-merge_events_by_keys_missing SHAPE -containment-p0-nested_call_args CORE_164 containment-p0-tag_list_names SHAPE -containment-p0-union_no_overlap CORE_161 -containment-p0-union_no_overlap_flooded CORE_161 data-shapes-p0-chunk_events_by_key SHAPE -data-shapes-p0-flood_pulsetime RUST_746 data-shapes-p0-merge_events_by_keys_app SHAPE data-shapes-p0-merge_events_by_keys_app_title SHAPE data-shapes-p0-merge_events_by_keys_missing SHAPE -data-shapes-p0-nested_call_args CORE_164 data-shapes-p0-split_url_events SHAPE data-shapes-p0-tag_list_names SHAPE -data-shapes-p0-union_no_overlap_rev CORE_161 dst-p0-chunk_events_by_key SHAPE dst-p0-flood CORE_165 -dst-p0-flood_pulsetime CORE_165,RUST_746 +dst-p0-flood_pulsetime CORE_165 dst-p0-merge_events_by_keys_app SHAPE dst-p0-merge_events_by_keys_missing SHAPE -dst-p0-nested_call_args CORE_164 dst-p0-tag_list_names SHAPE dst-p0-union_no_overlap_flooded CORE_165 -dst-p0-union_no_overlap_rev CORE_161 -dst-p0-union_no_overlap_unsorted CORE_161 -dst-p1-categorize CORE_162 dst-p1-chunk_events_by_key SHAPE -dst-p1-concat CORE_162 -dst-p1-exclude_keyvals CORE_162 -dst-p1-filter_period_intersect CORE_162 -dst-p1-filter_period_intersect_rev CORE_162 -dst-p1-filter_period_intersect_unsorted CORE_162 -dst-p1-find_bucket CORE_162 -dst-p1-flood CORE_162 -dst-p1-flood_pulsetime CORE_162,RUST_746 -dst-p1-limit_events CORE_162 dst-p1-merge_events_by_keys_app SHAPE -dst-p1-merge_events_by_keys_app_title CORE_162 dst-p1-merge_events_by_keys_missing SHAPE -dst-p1-nested_call_args CORE_164 -dst-p1-period_union CORE_162 -dst-p1-query_bucket CORE_162 -dst-p1-sort_by_duration CORE_162 -dst-p1-sort_by_timestamp CORE_162 -dst-p1-split_url_events CORE_162 -dst-p1-sum_durations CORE_162 -dst-p1-tag CORE_162 dst-p1-tag_list_names SHAPE -dst-p1-union_no_overlap CORE_162 -dst-p1-union_no_overlap_empty CORE_162 -dst-p1-union_no_overlap_flooded CORE_162 -dst-p1-union_no_overlap_rev CORE_161,CORE_162 -dst-p1-union_no_overlap_unsorted CORE_162 empty-b-p0-chunk_events_by_key SHAPE -empty-b-p0-flood_pulsetime RUST_746 empty-b-p0-merge_events_by_keys_app SHAPE empty-b-p0-merge_events_by_keys_missing SHAPE -empty-b-p0-nested_call_args CORE_164 empty-b-p0-tag_list_names SHAPE flood-gaps-p0-chunk_events_by_key SHAPE -flood-gaps-p0-flood RUST_746 -flood-gaps-p0-flood_pulsetime CORE_165,RUST_746 +flood-gaps-p0-flood_pulsetime CORE_165 flood-gaps-p0-merge_events_by_keys_app SHAPE flood-gaps-p0-merge_events_by_keys_missing SHAPE -flood-gaps-p0-nested_call_args CORE_164 flood-gaps-p0-tag_list_names SHAPE -flood-gaps-p0-union_no_overlap CORE_161 -flood-gaps-p0-union_no_overlap_flooded CORE_161,RUST_746 fpi-adjacent-filters-p0-chunk_events_by_key SHAPE -fpi-adjacent-filters-p0-flood_pulsetime RUST_746 fpi-adjacent-filters-p0-merge_events_by_keys_app SHAPE fpi-adjacent-filters-p0-merge_events_by_keys_missing SHAPE -fpi-adjacent-filters-p0-nested_call_args CORE_164 fpi-adjacent-filters-p0-tag_list_names SHAPE -fpi-adjacent-filters-p0-union_no_overlap CORE_161 -fpi-adjacent-filters-p0-union_no_overlap_flooded CORE_161 fpi-zero-at-event-end-p0-chunk_events_by_key SHAPE -fpi-zero-at-event-end-p0-filter_period_intersect_rev RUST_747 -fpi-zero-at-event-end-p0-flood_pulsetime RUST_746 fpi-zero-at-event-end-p0-merge_events_by_keys_app SHAPE fpi-zero-at-event-end-p0-merge_events_by_keys_missing SHAPE -fpi-zero-at-event-end-p0-nested_call_args CORE_164 fpi-zero-at-event-end-p0-tag_list_names SHAPE -fpi-zero-at-event-end-p0-union_no_overlap_flooded RUST_746 -fpi-zero-at-event-end-p0-union_no_overlap_unsorted CORE_161 fpi-zero-at-filter-end-p0-chunk_events_by_key SHAPE -fpi-zero-at-filter-end-p0-filter_period_intersect RUST_747 -fpi-zero-at-filter-end-p0-filter_period_intersect_rev RUST_747 -fpi-zero-at-filter-end-p0-filter_period_intersect_unsorted RUST_747 -fpi-zero-at-filter-end-p0-flood_pulsetime RUST_746 fpi-zero-at-filter-end-p0-merge_events_by_keys_app SHAPE fpi-zero-at-filter-end-p0-merge_events_by_keys_missing SHAPE -fpi-zero-at-filter-end-p0-nested_call_args CORE_164 fpi-zero-at-filter-end-p0-tag_list_names SHAPE -fpi-zero-at-filter-end-p0-union_no_overlap_flooded RUST_744 | RUST_746 -fpi-zero-at-filter-end-p0-union_no_overlap_rev CORE_161 -fpi-zero-at-filter-end-p0-union_no_overlap_unsorted CORE_161,RUST_744 fractional-ms-p0-chunk_events_by_key PRECISION,SHAPE fractional-ms-p0-flood CORE_165 -fractional-ms-p0-flood_pulsetime CORE_165,RUST_746 +fractional-ms-p0-flood_pulsetime CORE_165 fractional-ms-p0-merge_events_by_keys_app PRECISION,SHAPE fractional-ms-p0-merge_events_by_keys_missing PRECISION,SHAPE -fractional-ms-p0-nested_call_args CORE_164 fractional-ms-p0-tag_list_names PRECISION,SHAPE fractional-ms-p0-union_no_overlap_flooded CORE_165 fractional-p0-categorize PRECISION @@ -134,7 +75,6 @@ fractional-p0-query_bucket PRECISION fractional-p0-sort_by_duration PRECISION fractional-p0-sort_by_timestamp PRECISION fractional-p0-split_url_events PRECISION,SHAPE -fractional-p0-sum_durations RUST_745 fractional-p0-tag PRECISION,SHAPE fractional-p0-tag_list_names PRECISION,SHAPE fractional-p0-union_no_overlap PRECISION @@ -142,87 +82,18 @@ fractional-p0-union_no_overlap_empty PRECISION fractional-p0-union_no_overlap_flooded PRECISION fractional-p0-union_no_overlap_rev PRECISION fractional-p0-union_no_overlap_unsorted PRECISION -identical-p0-categorize CORE_163 identical-p0-chunk_events_by_key SHAPE -identical-p0-concat CORE_163 -identical-p0-filter_keyvals_regex CORE_163 -identical-p0-filter_period_intersect CORE_163 -identical-p0-filter_period_intersect_unsorted CORE_163 -identical-p0-find_bucket CORE_163 -identical-p0-flood CORE_163,RUST_746 -identical-p0-flood_pulsetime CORE_163,RUST_746 -identical-p0-limit_events CORE_163 identical-p0-merge_events_by_keys_app SHAPE identical-p0-merge_events_by_keys_missing SHAPE -identical-p0-nested_call_args CORE_164 -identical-p0-query_bucket CORE_163 -identical-p0-sort_by_timestamp CORE_163 -identical-p0-split_url_events CORE_163 -identical-p0-tag CORE_163 identical-p0-tag_list_names SHAPE -identical-p0-union_no_overlap CORE_163 -identical-p0-union_no_overlap_empty CORE_163 -identical-p0-union_no_overlap_flooded CORE_163,RUST_746 -identical-p0-union_no_overlap_rev CORE_161,RUST_744 | CORE_163 -identical-p0-union_no_overlap_unsorted CORE_163 -period-clip-p0-categorize CORE_162 period-clip-p0-chunk_events_by_key SHAPE -period-clip-p0-concat CORE_162 -period-clip-p0-exclude_keyvals CORE_162 -period-clip-p0-filter_keyvals_regex CORE_162 -period-clip-p0-filter_period_intersect CORE_162 -period-clip-p0-filter_period_intersect_rev CORE_162 -period-clip-p0-filter_period_intersect_unsorted CORE_162 -period-clip-p0-find_bucket CORE_162 -period-clip-p0-flood CORE_162 -period-clip-p0-flood_pulsetime CORE_162,RUST_746 -period-clip-p0-limit_events CORE_162 period-clip-p0-merge_events_by_keys_app SHAPE -period-clip-p0-merge_events_by_keys_app_title CORE_162 period-clip-p0-merge_events_by_keys_missing SHAPE -period-clip-p0-nested_call_args CORE_164 -period-clip-p0-period_union CORE_162 -period-clip-p0-query_bucket CORE_162 -period-clip-p0-sort_by_duration CORE_162 -period-clip-p0-sort_by_timestamp CORE_162 -period-clip-p0-split_url_events CORE_162 -period-clip-p0-sum_durations CORE_162 -period-clip-p0-tag CORE_162 period-clip-p0-tag_list_names SHAPE -period-clip-p0-union_no_overlap CORE_162 -period-clip-p0-union_no_overlap_empty CORE_162 -period-clip-p0-union_no_overlap_flooded CORE_162 -period-clip-p0-union_no_overlap_rev CORE_162 -period-clip-p0-union_no_overlap_unsorted CORE_162 -period-clip-p1-categorize CORE_162 period-clip-p1-chunk_events_by_key SHAPE -period-clip-p1-concat CORE_162 -period-clip-p1-exclude_keyvals CORE_162 -period-clip-p1-filter_keyvals_regex CORE_162 -period-clip-p1-filter_period_intersect CORE_162 -period-clip-p1-filter_period_intersect_rev CORE_162 -period-clip-p1-filter_period_intersect_unsorted CORE_162 -period-clip-p1-find_bucket CORE_162 -period-clip-p1-flood CORE_162 -period-clip-p1-flood_pulsetime CORE_162,RUST_746 -period-clip-p1-limit_events CORE_162 period-clip-p1-merge_events_by_keys_app SHAPE -period-clip-p1-merge_events_by_keys_app_title CORE_162 period-clip-p1-merge_events_by_keys_missing SHAPE -period-clip-p1-nested_call_args CORE_164 -period-clip-p1-period_union CORE_162 -period-clip-p1-query_bucket CORE_162 -period-clip-p1-sort_by_duration CORE_162 -period-clip-p1-sort_by_timestamp CORE_162 -period-clip-p1-split_url_events CORE_162 -period-clip-p1-sum_durations CORE_162 -period-clip-p1-tag CORE_162 period-clip-p1-tag_list_names SHAPE -period-clip-p1-union_no_overlap CORE_162 -period-clip-p1-union_no_overlap_empty CORE_162 -period-clip-p1-union_no_overlap_flooded CORE_162 -period-clip-p1-union_no_overlap_rev CORE_162 -period-clip-p1-union_no_overlap_unsorted CORE_162 period-clip-us-p0-categorize PRECISION period-clip-us-p0-chunk_events_by_key PRECISION,SHAPE period-clip-us-p0-concat PRECISION @@ -253,493 +124,263 @@ period-clip-us-p0-union_no_overlap_empty PRECISION period-clip-us-p0-union_no_overlap_flooded PRECISION period-clip-us-p0-union_no_overlap_rev PRECISION period-clip-us-p0-union_no_overlap_unsorted PRECISION -python:containment-p0-union_no_overlap:measure_ab CORE_161 -python:containment-p0-union_no_overlap:no_overlap CORE_161 -python:containment-p0-union_no_overlap_flooded:no_overlap CORE_161 python:data-shapes-p0-merge_events_by_keys_app:merge_conserves_app SHAPE -python:data-shapes-p0-union_no_overlap_rev:measure_ab CORE_161 -python:data-shapes-p0-union_no_overlap_rev:no_overlap CORE_161 -python:dst-p0-union_no_overlap_rev:measure_ab CORE_161 -python:dst-p0-union_no_overlap_rev:no_overlap CORE_161 -python:dst-p1-union_no_overlap_rev:measure_ab CORE_161 -python:dst-p1-union_no_overlap_rev:no_overlap CORE_161 -python:fpi-adjacent-filters-p0-union_no_overlap:measure_ab CORE_161 -python:fpi-adjacent-filters-p0-union_no_overlap:no_overlap CORE_161 -python:fpi-adjacent-filters-p0-union_no_overlap_flooded:no_overlap CORE_161 -python:fpi-zero-at-filter-end-p0-union_no_overlap:no_overlap CORE_161 -python:fpi-zero-at-filter-end-p0-union_no_overlap_rev:measure_ab CORE_161 python:fractional-p0-union_no_overlap_flooded:no_overlap CORE_165 -python:period-clip-us-p0-union_no_overlap_rev:measure_ab CORE_161 -python:seed00-p0-multidevice:md_no_overlap CORE_161 -python:seed00-p1-multidevice:md_no_overlap CORE_161 -python:seed01-p0-multidevice:md_no_overlap CORE_161 -python:seed01-p1-multidevice:md_no_overlap CORE_161 -python:seed02-p0-multidevice:md_no_overlap CORE_161 -python:seed02-p1-multidevice:md_no_overlap CORE_161 -python:seed03-p0-multidevice:md_no_overlap CORE_161 -python:seed03-p1-multidevice:md_no_overlap CORE_161 -python:seed04-p0-multidevice:md_no_overlap CORE_161 -python:seed04-p1-multidevice:md_no_overlap CORE_161 -python:seed05-p0-multidevice:md_no_overlap CORE_161 -python:seed05-p1-multidevice:md_no_overlap CORE_161 -python:seed06-p0-multidevice:md_no_overlap CORE_161 -python:seed06-p1-multidevice:md_no_overlap CORE_161 -python:seed07-p0-multidevice:md_no_overlap CORE_161 -python:seed07-p1-multidevice:md_no_overlap CORE_161 -python:seed08-p0-multidevice:md_no_overlap CORE_161 -python:seed08-p1-multidevice:md_no_overlap CORE_161 -python:seed09-p0-multidevice:md_no_overlap CORE_161 -python:seed09-p1-multidevice:md_no_overlap CORE_161 -python:seed10-p0-multidevice:md_no_overlap CORE_161 -python:seed10-p1-multidevice:md_no_overlap CORE_161 -python:seed11-p0-multidevice:md_no_overlap CORE_161 -python:seed11-p1-multidevice:md_no_overlap CORE_161 -python:straddle-p0-union_no_overlap_rev:measure_ab CORE_161 -python:straddle-p0-union_no_overlap_rev:no_overlap CORE_161 -python:unsorted-p0-union_no_overlap_rev:measure_ab CORE_161 -python:unsorted-p0-union_no_overlap_rev:no_overlap CORE_161 -python:zero-duration-p0-union_no_overlap:measure_ab CORE_161 -python:zero-duration-p0-union_no_overlap_rev:measure_ab CORE_161 -python:zero-duration-p0-union_no_overlap_rev:no_overlap CORE_161 -python:zero-inside-b-p0-union_no_overlap:measure_ab CORE_161 -python:zero-inside-b-p0-union_no_overlap:no_overlap CORE_161 -rust:flood-gaps-p0-flood:no_overlap_positive RUST_746 -rust:fpi-zero-at-filter-end-p0-union_no_overlap:no_overlap RUST_744 -rust:fpi-zero-at-filter-end-p0-union_no_overlap_flooded:no_overlap RUST_744 | RUST_746 -rust:fractional-p0-sum_durations:sum_matches_a RUST_745 -rust:identical-p0-flood:no_overlap_positive RUST_746 -rust:same-start-p0-flood:no_overlap_positive RUST_746 -rust:seed00-p0-flood:no_overlap_positive RUST_746 -rust:seed00-p1-flood:no_overlap_positive RUST_746 -rust:seed01-p0-flood:no_overlap_positive RUST_746 -rust:seed01-p0-sum_durations:sum_matches_a RUST_745 -rust:seed01-p1-flood:no_overlap_positive RUST_746 -rust:seed01-p1-sum_durations:sum_matches_a RUST_745 -rust:seed02-p0-flood:no_overlap_positive RUST_746 -rust:seed02-p0-sum_durations:sum_matches_a RUST_745 -rust:seed02-p1-flood:no_overlap_positive RUST_746 -rust:seed02-p1-sum_durations:sum_matches_a RUST_745 -rust:seed03-p0-flood:no_overlap_positive RUST_746 -rust:seed04-p0-flood:no_overlap_positive RUST_746 -rust:seed04-p1-flood:no_overlap_positive RUST_746 -rust:seed05-p0-flood:no_overlap_positive RUST_746 -rust:seed05-p0-sum_durations:sum_matches_a RUST_745 -rust:seed05-p1-flood:no_overlap_positive RUST_746 -rust:seed05-p1-sum_durations:sum_matches_a RUST_745 -rust:seed06-p0-flood:no_overlap_positive RUST_746 -rust:seed06-p1-flood:no_overlap_positive RUST_746 -rust:seed07-p0-flood:no_overlap_positive RUST_746 -rust:seed07-p0-sum_durations:sum_matches_a RUST_745 -rust:seed07-p1-flood:no_overlap_positive RUST_746 -rust:seed07-p1-sum_durations:sum_matches_a RUST_745 -rust:seed08-p0-flood:no_overlap_positive RUST_746 -rust:seed08-p1-flood:no_overlap_positive RUST_746 -rust:seed09-p0-flood:no_overlap_positive RUST_746 -rust:seed09-p1-flood:no_overlap_positive RUST_746 -rust:seed10-p0-flood:no_overlap_positive RUST_746 -rust:seed10-p1-flood:no_overlap_positive RUST_746 -rust:seed11-p0-flood:no_overlap_positive RUST_746 -rust:seed11-p1-flood:no_overlap_positive RUST_746 -rust:self-overlap-p0-flood:no_overlap_positive RUST_746 -rust:zero-duration-p0-union_no_overlap:measure_ab RUST_744 -rust:zero-duration-p0-union_no_overlap:no_overlap RUST_744 -rust:zero-duration-p0-union_no_overlap_rev:measure_ab RUST_744 -rust:zero-duration-p0-union_no_overlap_rev:no_overlap RUST_744 -rust:zero-inside-b-p0-union_no_overlap:measure_ab RUST_744 -rust:zero-inside-b-p0-union_no_overlap:no_overlap RUST_744 -same-start-p0-categorize CORE_163 same-start-p0-chunk_events_by_key SHAPE -same-start-p0-concat CORE_163 -same-start-p0-exclude_keyvals CORE_163 -same-start-p0-filter_keyvals_regex CORE_163 -same-start-p0-filter_period_intersect CORE_163,CORE_166,RUST_747 -same-start-p0-filter_period_intersect_rev CORE_163,CORE_166,RUST_747 -same-start-p0-filter_period_intersect_unsorted CORE_163,CORE_166,RUST_747 -same-start-p0-find_bucket CORE_163 -same-start-p0-flood CORE_163,RUST_746 -same-start-p0-flood_pulsetime CORE_163,RUST_746 -same-start-p0-limit_events CORE_163 +same-start-p0-filter_period_intersect CORE_166 +same-start-p0-filter_period_intersect_rev CORE_166 +same-start-p0-filter_period_intersect_unsorted CORE_166 same-start-p0-merge_events_by_keys_app SHAPE same-start-p0-merge_events_by_keys_missing SHAPE -same-start-p0-nested_call_args CORE_164 -same-start-p0-query_bucket CORE_163 -same-start-p0-sort_by_timestamp CORE_163 -same-start-p0-split_url_events CORE_163 -same-start-p0-tag CORE_163 same-start-p0-tag_list_names SHAPE -same-start-p0-union_no_overlap CORE_163 -same-start-p0-union_no_overlap_empty CORE_163 -same-start-p0-union_no_overlap_flooded CORE_163,RUST_746 -same-start-p0-union_no_overlap_rev CORE_161,CORE_163,RUST_744 -same-start-p0-union_no_overlap_unsorted CORE_163 seed00-p0-aw-client:canonicalEvents CORE_165 seed00-p0-aw-client:canonicalEvents-android SHAPE seed00-p0-aw-client:fullDesktopQuery SHAPE seed00-p0-aw-client:fullDesktopQuery+always_active SHAPE seed00-p0-aw-client:fullDesktopQuery+browser SHAPE seed00-p0-chunk_events_by_key SHAPE -seed00-p0-filter_period_intersect CORE_166,RUST_747 -seed00-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed00-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed00-p0-flood RUST_746 -seed00-p0-flood_pulsetime RUST_746 +seed00-p0-filter_period_intersect CORE_166 +seed00-p0-filter_period_intersect_rev CORE_166 +seed00-p0-filter_period_intersect_unsorted CORE_166 seed00-p0-merge_events_by_keys_app SHAPE seed00-p0-merge_events_by_keys_app_title SHAPE seed00-p0-merge_events_by_keys_missing SHAPE seed00-p0-multidevice SHAPE seed00-p0-multidevice-webui-android-merge SHAPE -seed00-p0-nested_call_args CORE_164 seed00-p0-split_url_events SHAPE seed00-p0-tag SHAPE seed00-p0-tag_list_names SHAPE -seed00-p0-union_no_overlap CORE_161,RUST_744 -seed00-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed00-p0-union_no_overlap_rev CORE_161,RUST_744 seed00-p1-aw-client:canonicalEvents CORE_165 seed00-p1-aw-client:canonicalEvents-android SHAPE seed00-p1-aw-client:fullDesktopQuery SHAPE seed00-p1-aw-client:fullDesktopQuery+always_active SHAPE seed00-p1-aw-client:fullDesktopQuery+browser SHAPE seed00-p1-chunk_events_by_key SHAPE -seed00-p1-filter_period_intersect CORE_166,RUST_747 -seed00-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed00-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed00-p1-flood RUST_746 -seed00-p1-flood_pulsetime RUST_746 +seed00-p1-filter_period_intersect CORE_166 +seed00-p1-filter_period_intersect_rev CORE_166 +seed00-p1-filter_period_intersect_unsorted CORE_166 seed00-p1-merge_events_by_keys_app SHAPE seed00-p1-merge_events_by_keys_app_title SHAPE seed00-p1-merge_events_by_keys_missing SHAPE seed00-p1-multidevice SHAPE seed00-p1-multidevice-webui-android-merge SHAPE -seed00-p1-nested_call_args CORE_164 seed00-p1-split_url_events SHAPE seed00-p1-tag SHAPE seed00-p1-tag_list_names SHAPE -seed00-p1-union_no_overlap CORE_161,RUST_744 -seed00-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed00-p1-union_no_overlap_rev CORE_161,RUST_744 seed01-p0-aw-client:canonicalEvents-android SHAPE seed01-p0-aw-client:fullDesktopQuery SHAPE seed01-p0-aw-client:fullDesktopQuery+always_active SHAPE seed01-p0-aw-client:fullDesktopQuery+browser SHAPE seed01-p0-chunk_events_by_key SHAPE -seed01-p0-filter_period_intersect CORE_166,RUST_747 -seed01-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed01-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed01-p0-flood RUST_746 -seed01-p0-flood_pulsetime RUST_746 +seed01-p0-filter_period_intersect CORE_166 +seed01-p0-filter_period_intersect_rev CORE_166 +seed01-p0-filter_period_intersect_unsorted CORE_166 seed01-p0-merge_events_by_keys_app SHAPE seed01-p0-merge_events_by_keys_app_title SHAPE seed01-p0-merge_events_by_keys_missing SHAPE seed01-p0-multidevice SHAPE seed01-p0-multidevice-webui-android-merge SHAPE -seed01-p0-nested_call_args CORE_164 seed01-p0-split_url_events SHAPE -seed01-p0-sum_durations RUST_745 seed01-p0-tag_list_names SHAPE -seed01-p0-union_no_overlap CORE_161,RUST_744 -seed01-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed01-p0-union_no_overlap_rev CORE_161,RUST_744 seed01-p1-aw-client:canonicalEvents-android SHAPE seed01-p1-aw-client:fullDesktopQuery SHAPE seed01-p1-aw-client:fullDesktopQuery+always_active SHAPE seed01-p1-aw-client:fullDesktopQuery+browser SHAPE seed01-p1-chunk_events_by_key SHAPE seed01-p1-filter_period_intersect CORE_166 -seed01-p1-filter_period_intersect_rev CORE_166,RUST_747 +seed01-p1-filter_period_intersect_rev CORE_166 seed01-p1-filter_period_intersect_unsorted CORE_166 -seed01-p1-flood RUST_746 -seed01-p1-flood_pulsetime RUST_746 seed01-p1-merge_events_by_keys_app SHAPE seed01-p1-merge_events_by_keys_app_title SHAPE seed01-p1-merge_events_by_keys_missing SHAPE seed01-p1-multidevice SHAPE seed01-p1-multidevice-webui-android-merge SHAPE -seed01-p1-nested_call_args CORE_164 seed01-p1-split_url_events SHAPE -seed01-p1-sum_durations RUST_745 seed01-p1-tag_list_names SHAPE -seed01-p1-union_no_overlap CORE_161,RUST_744 -seed01-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed01-p1-union_no_overlap_rev CORE_161,RUST_744 seed02-p0-aw-client:canonicalEvents CORE_165 seed02-p0-aw-client:canonicalEvents-android SHAPE seed02-p0-aw-client:fullDesktopQuery SHAPE seed02-p0-aw-client:fullDesktopQuery+always_active SHAPE seed02-p0-aw-client:fullDesktopQuery+browser SHAPE seed02-p0-chunk_events_by_key SHAPE -seed02-p0-filter_period_intersect CORE_166,RUST_747 -seed02-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed02-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed02-p0-flood RUST_746 -seed02-p0-flood_pulsetime RUST_746 +seed02-p0-filter_period_intersect CORE_166 +seed02-p0-filter_period_intersect_rev CORE_166 +seed02-p0-filter_period_intersect_unsorted CORE_166 seed02-p0-merge_events_by_keys_app SHAPE seed02-p0-merge_events_by_keys_app_title SHAPE seed02-p0-merge_events_by_keys_missing SHAPE seed02-p0-multidevice SHAPE seed02-p0-multidevice-webui-android-merge SHAPE -seed02-p0-nested_call_args CORE_164 -seed02-p0-period_union RUST_745 seed02-p0-split_url_events SHAPE -seed02-p0-sum_durations RUST_745 seed02-p0-tag SHAPE seed02-p0-tag_list_names SHAPE -seed02-p0-union_no_overlap CORE_161,RUST_744,RUST_745 -seed02-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed02-p0-union_no_overlap_rev CORE_161,RUST_744 seed02-p1-aw-client:canonicalEvents CORE_165 seed02-p1-aw-client:canonicalEvents-android SHAPE seed02-p1-aw-client:fullDesktopQuery SHAPE seed02-p1-aw-client:fullDesktopQuery+always_active SHAPE seed02-p1-aw-client:fullDesktopQuery+browser SHAPE seed02-p1-chunk_events_by_key SHAPE -seed02-p1-filter_period_intersect CORE_166,RUST_747 -seed02-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed02-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed02-p1-flood RUST_746 -seed02-p1-flood_pulsetime RUST_746 +seed02-p1-filter_period_intersect CORE_166 +seed02-p1-filter_period_intersect_rev CORE_166 +seed02-p1-filter_period_intersect_unsorted CORE_166 seed02-p1-merge_events_by_keys_app SHAPE seed02-p1-merge_events_by_keys_app_title SHAPE seed02-p1-merge_events_by_keys_missing SHAPE seed02-p1-multidevice SHAPE seed02-p1-multidevice-webui-android-merge SHAPE -seed02-p1-nested_call_args CORE_164 -seed02-p1-period_union RUST_745 seed02-p1-split_url_events SHAPE -seed02-p1-sum_durations RUST_745 seed02-p1-tag SHAPE seed02-p1-tag_list_names SHAPE -seed02-p1-union_no_overlap CORE_161,RUST_744,RUST_745 -seed02-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed02-p1-union_no_overlap_rev CORE_161,RUST_744 seed03-p0-aw-client:canonicalEvents CORE_165 seed03-p0-aw-client:canonicalEvents-android SHAPE seed03-p0-aw-client:fullDesktopQuery SHAPE seed03-p0-aw-client:fullDesktopQuery+always_active SHAPE seed03-p0-aw-client:fullDesktopQuery+browser SHAPE seed03-p0-chunk_events_by_key SHAPE -seed03-p0-filter_period_intersect CORE_166,RUST_747 -seed03-p0-filter_period_intersect_rev RUST_747 -seed03-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed03-p0-flood RUST_746 -seed03-p0-flood_pulsetime RUST_746 +seed03-p0-filter_period_intersect CORE_166 +seed03-p0-filter_period_intersect_unsorted CORE_166 seed03-p0-merge_events_by_keys_app SHAPE seed03-p0-merge_events_by_keys_app_title SHAPE seed03-p0-merge_events_by_keys_missing SHAPE seed03-p0-multidevice SHAPE seed03-p0-multidevice-webui-android-merge SHAPE -seed03-p0-nested_call_args CORE_164 seed03-p0-split_url_events SHAPE seed03-p0-tag SHAPE seed03-p0-tag_list_names SHAPE -seed03-p0-union_no_overlap CORE_161,RUST_744 -seed03-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed03-p0-union_no_overlap_rev CORE_161,RUST_744 seed03-p1-aw-client:canonicalEvents CORE_165 seed03-p1-aw-client:canonicalEvents-android SHAPE seed03-p1-aw-client:fullDesktopQuery SHAPE seed03-p1-aw-client:fullDesktopQuery+always_active SHAPE seed03-p1-aw-client:fullDesktopQuery+browser SHAPE seed03-p1-chunk_events_by_key SHAPE -seed03-p1-filter_period_intersect CORE_166,RUST_747 -seed03-p1-filter_period_intersect_rev RUST_747 -seed03-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed03-p1-flood RUST_746 -seed03-p1-flood_pulsetime RUST_746 +seed03-p1-filter_period_intersect CORE_166 +seed03-p1-filter_period_intersect_unsorted CORE_166 seed03-p1-merge_events_by_keys_app SHAPE seed03-p1-merge_events_by_keys_app_title SHAPE seed03-p1-merge_events_by_keys_missing SHAPE seed03-p1-multidevice SHAPE seed03-p1-multidevice-webui-android-merge SHAPE -seed03-p1-nested_call_args CORE_164 seed03-p1-split_url_events SHAPE seed03-p1-tag SHAPE seed03-p1-tag_list_names SHAPE -seed03-p1-union_no_overlap CORE_161,RUST_744 -seed03-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed03-p1-union_no_overlap_rev CORE_161,RUST_744 seed04-p0-aw-client:canonicalEvents-android SHAPE seed04-p0-aw-client:fullDesktopQuery SHAPE seed04-p0-aw-client:fullDesktopQuery+always_active SHAPE seed04-p0-aw-client:fullDesktopQuery+browser SHAPE seed04-p0-chunk_events_by_key SHAPE -seed04-p0-filter_period_intersect CORE_166,RUST_747 -seed04-p0-filter_period_intersect_rev RUST_747 -seed04-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed04-p0-flood RUST_746 -seed04-p0-flood_pulsetime RUST_746 +seed04-p0-filter_period_intersect CORE_166 +seed04-p0-filter_period_intersect_unsorted CORE_166 seed04-p0-merge_events_by_keys_app SHAPE seed04-p0-merge_events_by_keys_app_title SHAPE seed04-p0-merge_events_by_keys_missing SHAPE seed04-p0-multidevice SHAPE seed04-p0-multidevice-webui-android-merge SHAPE -seed04-p0-nested_call_args CORE_164 seed04-p0-split_url_events SHAPE seed04-p0-tag SHAPE seed04-p0-tag_list_names SHAPE -seed04-p0-union_no_overlap CORE_161,RUST_744 -seed04-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed04-p0-union_no_overlap_rev CORE_161,RUST_744 seed04-p1-aw-client:canonicalEvents-android SHAPE seed04-p1-aw-client:fullDesktopQuery SHAPE seed04-p1-aw-client:fullDesktopQuery+always_active SHAPE seed04-p1-aw-client:fullDesktopQuery+browser SHAPE seed04-p1-chunk_events_by_key SHAPE -seed04-p1-filter_period_intersect CORE_166,RUST_747 -seed04-p1-filter_period_intersect_rev RUST_747 -seed04-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed04-p1-flood RUST_746 -seed04-p1-flood_pulsetime RUST_746 +seed04-p1-filter_period_intersect CORE_166 +seed04-p1-filter_period_intersect_unsorted CORE_166 seed04-p1-merge_events_by_keys_app SHAPE seed04-p1-merge_events_by_keys_app_title SHAPE seed04-p1-merge_events_by_keys_missing SHAPE seed04-p1-multidevice SHAPE seed04-p1-multidevice-webui-android-merge SHAPE -seed04-p1-nested_call_args CORE_164 seed04-p1-split_url_events SHAPE seed04-p1-tag SHAPE seed04-p1-tag_list_names SHAPE -seed04-p1-union_no_overlap CORE_161,RUST_744 -seed04-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed04-p1-union_no_overlap_rev CORE_161,RUST_744 seed05-p0-aw-client:canonicalEvents-android SHAPE seed05-p0-aw-client:fullDesktopQuery SHAPE seed05-p0-aw-client:fullDesktopQuery+always_active SHAPE seed05-p0-aw-client:fullDesktopQuery+browser SHAPE seed05-p0-chunk_events_by_key SHAPE -seed05-p0-filter_period_intersect RUST_747 -seed05-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed05-p0-filter_period_intersect_unsorted RUST_747 -seed05-p0-flood RUST_746 -seed05-p0-flood_pulsetime RUST_746 +seed05-p0-filter_period_intersect_rev CORE_166 seed05-p0-merge_events_by_keys_app SHAPE seed05-p0-merge_events_by_keys_app_title SHAPE seed05-p0-merge_events_by_keys_missing SHAPE seed05-p0-multidevice SHAPE seed05-p0-multidevice-webui-android-merge SHAPE -seed05-p0-nested_call_args CORE_164 seed05-p0-split_url_events SHAPE -seed05-p0-sum_durations RUST_745 seed05-p0-tag SHAPE seed05-p0-tag_list_names SHAPE -seed05-p0-union_no_overlap CORE_161,RUST_744,RUST_745 -seed05-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed05-p0-union_no_overlap_rev CORE_161,RUST_744,RUST_745 seed05-p1-aw-client:canonicalEvents-android SHAPE seed05-p1-aw-client:fullDesktopQuery SHAPE seed05-p1-aw-client:fullDesktopQuery+always_active SHAPE seed05-p1-aw-client:fullDesktopQuery+browser SHAPE seed05-p1-chunk_events_by_key SHAPE -seed05-p1-filter_period_intersect RUST_747 -seed05-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed05-p1-filter_period_intersect_unsorted RUST_747 -seed05-p1-flood RUST_746 -seed05-p1-flood_pulsetime RUST_746 +seed05-p1-filter_period_intersect_rev CORE_166 seed05-p1-merge_events_by_keys_app SHAPE seed05-p1-merge_events_by_keys_app_title SHAPE seed05-p1-merge_events_by_keys_missing SHAPE seed05-p1-multidevice SHAPE seed05-p1-multidevice-webui-android-merge SHAPE -seed05-p1-nested_call_args CORE_164 seed05-p1-split_url_events SHAPE -seed05-p1-sum_durations RUST_745 seed05-p1-tag SHAPE seed05-p1-tag_list_names SHAPE -seed05-p1-union_no_overlap CORE_161,RUST_744,RUST_745 -seed05-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed05-p1-union_no_overlap_rev CORE_161,RUST_744,RUST_745 seed06-p0-aw-client:canonicalEvents-android SHAPE seed06-p0-aw-client:fullDesktopQuery SHAPE seed06-p0-aw-client:fullDesktopQuery+always_active SHAPE seed06-p0-aw-client:fullDesktopQuery+browser SHAPE seed06-p0-chunk_events_by_key SHAPE -seed06-p0-filter_period_intersect RUST_747 -seed06-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed06-p0-filter_period_intersect_unsorted RUST_747 -seed06-p0-flood RUST_746 -seed06-p0-flood_pulsetime RUST_746 +seed06-p0-filter_period_intersect_rev CORE_166 seed06-p0-merge_events_by_keys_app SHAPE seed06-p0-merge_events_by_keys_app_title SHAPE seed06-p0-merge_events_by_keys_missing SHAPE seed06-p0-multidevice SHAPE seed06-p0-multidevice-webui-android-merge SHAPE -seed06-p0-nested_call_args CORE_164 seed06-p0-split_url_events SHAPE seed06-p0-tag SHAPE seed06-p0-tag_list_names SHAPE -seed06-p0-union_no_overlap CORE_161,RUST_744 -seed06-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed06-p0-union_no_overlap_rev CORE_161,RUST_744 seed06-p1-aw-client:canonicalEvents-android SHAPE seed06-p1-aw-client:fullDesktopQuery SHAPE seed06-p1-aw-client:fullDesktopQuery+always_active SHAPE seed06-p1-aw-client:fullDesktopQuery+browser SHAPE seed06-p1-chunk_events_by_key SHAPE -seed06-p1-filter_period_intersect RUST_747 -seed06-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed06-p1-filter_period_intersect_unsorted RUST_747 -seed06-p1-flood RUST_746 -seed06-p1-flood_pulsetime RUST_746 +seed06-p1-filter_period_intersect_rev CORE_166 seed06-p1-merge_events_by_keys_app SHAPE seed06-p1-merge_events_by_keys_app_title SHAPE seed06-p1-merge_events_by_keys_missing SHAPE seed06-p1-multidevice SHAPE seed06-p1-multidevice-webui-android-merge SHAPE -seed06-p1-nested_call_args CORE_164 seed06-p1-split_url_events SHAPE seed06-p1-tag SHAPE seed06-p1-tag_list_names SHAPE -seed06-p1-union_no_overlap CORE_161,RUST_744 -seed06-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed06-p1-union_no_overlap_rev CORE_161,RUST_744 seed07-p0-aw-client:canonicalEvents-android SHAPE seed07-p0-aw-client:fullDesktopQuery SHAPE seed07-p0-aw-client:fullDesktopQuery+always_active SHAPE seed07-p0-aw-client:fullDesktopQuery+browser SHAPE seed07-p0-chunk_events_by_key SHAPE -seed07-p0-filter_period_intersect CORE_166,RUST_747 -seed07-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed07-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed07-p0-flood RUST_746 -seed07-p0-flood_pulsetime RUST_746 +seed07-p0-filter_period_intersect CORE_166 +seed07-p0-filter_period_intersect_rev CORE_166 +seed07-p0-filter_period_intersect_unsorted CORE_166 seed07-p0-merge_events_by_keys_app SHAPE seed07-p0-merge_events_by_keys_app_title SHAPE seed07-p0-merge_events_by_keys_missing SHAPE seed07-p0-multidevice SHAPE seed07-p0-multidevice-webui-android-merge SHAPE -seed07-p0-nested_call_args CORE_164 seed07-p0-split_url_events SHAPE -seed07-p0-sum_durations RUST_745 seed07-p0-tag SHAPE seed07-p0-tag_list_names SHAPE -seed07-p0-union_no_overlap CORE_161,RUST_744 -seed07-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed07-p0-union_no_overlap_rev CORE_161,RUST_744 -seed07-p0-union_no_overlap_unsorted CORE_161,RUST_744 seed07-p1-aw-client:canonicalEvents-android SHAPE seed07-p1-aw-client:fullDesktopQuery SHAPE seed07-p1-aw-client:fullDesktopQuery+always_active SHAPE seed07-p1-aw-client:fullDesktopQuery+browser SHAPE seed07-p1-chunk_events_by_key SHAPE -seed07-p1-filter_period_intersect CORE_166,RUST_747 -seed07-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed07-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed07-p1-flood RUST_746 -seed07-p1-flood_pulsetime RUST_746 +seed07-p1-filter_period_intersect CORE_166 +seed07-p1-filter_period_intersect_rev CORE_166 +seed07-p1-filter_period_intersect_unsorted CORE_166 seed07-p1-merge_events_by_keys_app SHAPE seed07-p1-merge_events_by_keys_app_title SHAPE seed07-p1-merge_events_by_keys_missing SHAPE seed07-p1-multidevice SHAPE seed07-p1-multidevice-webui-android-merge SHAPE -seed07-p1-nested_call_args CORE_164 seed07-p1-split_url_events SHAPE -seed07-p1-sum_durations RUST_745 seed07-p1-tag SHAPE seed07-p1-tag_list_names SHAPE -seed07-p1-union_no_overlap CORE_161,RUST_744 -seed07-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed07-p1-union_no_overlap_rev CORE_161,RUST_744 -seed07-p1-union_no_overlap_unsorted CORE_161,RUST_744 seed08-p0-aw-client:canonicalEvents CORE_165 seed08-p0-aw-client:canonicalEvents-android SHAPE seed08-p0-aw-client:fullDesktopQuery SHAPE @@ -747,219 +388,145 @@ seed08-p0-aw-client:fullDesktopQuery+always_active SHAPE seed08-p0-aw-client:fullDesktopQuery+browser SHAPE seed08-p0-chunk_events_by_key SHAPE seed08-p0-filter_period_intersect CORE_166 -seed08-p0-filter_period_intersect_rev RUST_747 seed08-p0-filter_period_intersect_unsorted CORE_166 -seed08-p0-flood RUST_746 -seed08-p0-flood_pulsetime RUST_746 seed08-p0-merge_events_by_keys_app SHAPE seed08-p0-merge_events_by_keys_app_title SHAPE seed08-p0-merge_events_by_keys_missing SHAPE seed08-p0-multidevice SHAPE seed08-p0-multidevice-webui-android-merge SHAPE -seed08-p0-nested_call_args CORE_164 seed08-p0-split_url_events SHAPE seed08-p0-tag SHAPE seed08-p0-tag_list_names SHAPE -seed08-p0-union_no_overlap CORE_161,RUST_744 -seed08-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed08-p0-union_no_overlap_rev CORE_161,RUST_744 seed08-p1-aw-client:canonicalEvents CORE_165 seed08-p1-aw-client:canonicalEvents-android SHAPE seed08-p1-aw-client:fullDesktopQuery SHAPE seed08-p1-aw-client:fullDesktopQuery+always_active SHAPE seed08-p1-aw-client:fullDesktopQuery+browser SHAPE seed08-p1-chunk_events_by_key SHAPE -seed08-p1-flood RUST_746 -seed08-p1-flood_pulsetime RUST_746 seed08-p1-merge_events_by_keys_app SHAPE seed08-p1-merge_events_by_keys_app_title SHAPE seed08-p1-merge_events_by_keys_missing SHAPE seed08-p1-multidevice SHAPE seed08-p1-multidevice-webui-android-merge SHAPE -seed08-p1-nested_call_args CORE_164 seed08-p1-split_url_events SHAPE seed08-p1-tag SHAPE seed08-p1-tag_list_names SHAPE -seed08-p1-union_no_overlap_flooded RUST_746 seed09-p0-aw-client:canonicalEvents-android SHAPE seed09-p0-aw-client:fullDesktopQuery SHAPE seed09-p0-aw-client:fullDesktopQuery+always_active SHAPE seed09-p0-aw-client:fullDesktopQuery+browser SHAPE seed09-p0-chunk_events_by_key SHAPE -seed09-p0-filter_period_intersect CORE_166,RUST_747 -seed09-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed09-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed09-p0-flood RUST_746 -seed09-p0-flood_pulsetime RUST_746 +seed09-p0-filter_period_intersect CORE_166 +seed09-p0-filter_period_intersect_rev CORE_166 +seed09-p0-filter_period_intersect_unsorted CORE_166 seed09-p0-merge_events_by_keys_app SHAPE seed09-p0-merge_events_by_keys_app_title SHAPE seed09-p0-merge_events_by_keys_missing SHAPE seed09-p0-multidevice SHAPE seed09-p0-multidevice-webui-android-merge SHAPE -seed09-p0-nested_call_args CORE_164 seed09-p0-split_url_events SHAPE seed09-p0-tag SHAPE seed09-p0-tag_list_names SHAPE -seed09-p0-union_no_overlap CORE_161,RUST_744 -seed09-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed09-p0-union_no_overlap_rev CORE_161,RUST_744 seed09-p1-aw-client:canonicalEvents-android SHAPE seed09-p1-aw-client:fullDesktopQuery SHAPE seed09-p1-aw-client:fullDesktopQuery+always_active SHAPE seed09-p1-aw-client:fullDesktopQuery+browser SHAPE seed09-p1-chunk_events_by_key SHAPE -seed09-p1-filter_period_intersect CORE_166,RUST_747 -seed09-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed09-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed09-p1-flood RUST_746 -seed09-p1-flood_pulsetime RUST_746 +seed09-p1-filter_period_intersect CORE_166 +seed09-p1-filter_period_intersect_rev CORE_166 +seed09-p1-filter_period_intersect_unsorted CORE_166 seed09-p1-merge_events_by_keys_app SHAPE seed09-p1-merge_events_by_keys_app_title SHAPE seed09-p1-merge_events_by_keys_missing SHAPE seed09-p1-multidevice SHAPE seed09-p1-multidevice-webui-android-merge SHAPE -seed09-p1-nested_call_args CORE_164 seed09-p1-split_url_events SHAPE seed09-p1-tag SHAPE seed09-p1-tag_list_names SHAPE -seed09-p1-union_no_overlap CORE_161,RUST_744 -seed09-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed09-p1-union_no_overlap_rev CORE_161,RUST_744 seed10-p0-aw-client:canonicalEvents-android SHAPE seed10-p0-aw-client:fullDesktopQuery SHAPE seed10-p0-aw-client:fullDesktopQuery+always_active SHAPE seed10-p0-aw-client:fullDesktopQuery+browser SHAPE seed10-p0-chunk_events_by_key SHAPE -seed10-p0-filter_period_intersect CORE_166,RUST_747 -seed10-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed10-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed10-p0-flood RUST_746 -seed10-p0-flood_pulsetime RUST_746 +seed10-p0-filter_period_intersect CORE_166 +seed10-p0-filter_period_intersect_rev CORE_166 +seed10-p0-filter_period_intersect_unsorted CORE_166 seed10-p0-merge_events_by_keys_app SHAPE seed10-p0-merge_events_by_keys_app_title SHAPE seed10-p0-merge_events_by_keys_missing SHAPE seed10-p0-multidevice SHAPE seed10-p0-multidevice-webui-android-merge SHAPE -seed10-p0-nested_call_args CORE_164 seed10-p0-split_url_events SHAPE seed10-p0-tag SHAPE seed10-p0-tag_list_names SHAPE -seed10-p0-union_no_overlap CORE_161,RUST_744 -seed10-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed10-p0-union_no_overlap_rev CORE_161,RUST_744 seed10-p1-aw-client:canonicalEvents-android SHAPE seed10-p1-aw-client:fullDesktopQuery SHAPE seed10-p1-aw-client:fullDesktopQuery+always_active SHAPE seed10-p1-aw-client:fullDesktopQuery+browser SHAPE seed10-p1-chunk_events_by_key SHAPE -seed10-p1-filter_period_intersect CORE_166,RUST_747 -seed10-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed10-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed10-p1-flood RUST_746 -seed10-p1-flood_pulsetime RUST_746 +seed10-p1-filter_period_intersect CORE_166 +seed10-p1-filter_period_intersect_rev CORE_166 +seed10-p1-filter_period_intersect_unsorted CORE_166 seed10-p1-merge_events_by_keys_app SHAPE seed10-p1-merge_events_by_keys_app_title SHAPE seed10-p1-merge_events_by_keys_missing SHAPE seed10-p1-multidevice SHAPE seed10-p1-multidevice-webui-android-merge SHAPE -seed10-p1-nested_call_args CORE_164 seed10-p1-split_url_events SHAPE seed10-p1-tag SHAPE seed10-p1-tag_list_names SHAPE -seed10-p1-union_no_overlap CORE_161,RUST_744 -seed10-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed10-p1-union_no_overlap_rev CORE_161,RUST_744 seed11-p0-aw-client:canonicalEvents CORE_165 seed11-p0-aw-client:canonicalEvents-android SHAPE seed11-p0-aw-client:fullDesktopQuery SHAPE seed11-p0-aw-client:fullDesktopQuery+always_active SHAPE seed11-p0-aw-client:fullDesktopQuery+browser SHAPE seed11-p0-chunk_events_by_key SHAPE -seed11-p0-filter_period_intersect CORE_166,RUST_747 -seed11-p0-filter_period_intersect_rev CORE_166,RUST_747 -seed11-p0-filter_period_intersect_unsorted CORE_166,RUST_747 -seed11-p0-flood RUST_746 -seed11-p0-flood_pulsetime RUST_746 +seed11-p0-filter_period_intersect CORE_166 +seed11-p0-filter_period_intersect_rev CORE_166 +seed11-p0-filter_period_intersect_unsorted CORE_166 seed11-p0-merge_events_by_keys_app SHAPE seed11-p0-merge_events_by_keys_app_title SHAPE seed11-p0-merge_events_by_keys_missing SHAPE seed11-p0-multidevice SHAPE seed11-p0-multidevice-webui-android-merge SHAPE -seed11-p0-nested_call_args CORE_164 seed11-p0-split_url_events SHAPE seed11-p0-tag SHAPE seed11-p0-tag_list_names SHAPE -seed11-p0-union_no_overlap CORE_161,RUST_744 -seed11-p0-union_no_overlap_flooded CORE_161,RUST_746 -seed11-p0-union_no_overlap_rev CORE_161,RUST_744 seed11-p1-aw-client:canonicalEvents CORE_165 seed11-p1-aw-client:canonicalEvents-android SHAPE seed11-p1-aw-client:fullDesktopQuery SHAPE seed11-p1-aw-client:fullDesktopQuery+always_active SHAPE seed11-p1-aw-client:fullDesktopQuery+browser SHAPE seed11-p1-chunk_events_by_key SHAPE -seed11-p1-filter_period_intersect CORE_166,RUST_747 -seed11-p1-filter_period_intersect_rev CORE_166,RUST_747 -seed11-p1-filter_period_intersect_unsorted CORE_166,RUST_747 -seed11-p1-flood RUST_746 -seed11-p1-flood_pulsetime RUST_746 +seed11-p1-filter_period_intersect CORE_166 +seed11-p1-filter_period_intersect_rev CORE_166 +seed11-p1-filter_period_intersect_unsorted CORE_166 seed11-p1-merge_events_by_keys_app SHAPE seed11-p1-merge_events_by_keys_app_title SHAPE seed11-p1-merge_events_by_keys_missing SHAPE seed11-p1-multidevice SHAPE seed11-p1-multidevice-webui-android-merge SHAPE -seed11-p1-nested_call_args CORE_164 seed11-p1-split_url_events SHAPE seed11-p1-tag SHAPE seed11-p1-tag_list_names SHAPE -seed11-p1-union_no_overlap CORE_161,RUST_744 -seed11-p1-union_no_overlap_flooded CORE_161,RUST_746 -seed11-p1-union_no_overlap_rev CORE_161,RUST_744 self-overlap-p0-chunk_events_by_key SHAPE -self-overlap-p0-flood RUST_746 -self-overlap-p0-flood_pulsetime RUST_746 self-overlap-p0-merge_events_by_keys_app SHAPE self-overlap-p0-merge_events_by_keys_missing SHAPE -self-overlap-p0-nested_call_args CORE_164 self-overlap-p0-tag_list_names SHAPE -self-overlap-p0-union_no_overlap_flooded CORE_161,RUST_746 straddle-p0-chunk_events_by_key SHAPE -straddle-p0-flood_pulsetime RUST_746 straddle-p0-merge_events_by_keys_app SHAPE straddle-p0-merge_events_by_keys_missing SHAPE -straddle-p0-nested_call_args CORE_164 straddle-p0-tag_list_names SHAPE -straddle-p0-union_no_overlap_rev CORE_161 unsorted-p0-chunk_events_by_key SHAPE -unsorted-p0-flood RUST_746 -unsorted-p0-flood_pulsetime CORE_165,RUST_746 +unsorted-p0-flood_pulsetime CORE_165 unsorted-p0-merge_events_by_keys_app SHAPE unsorted-p0-merge_events_by_keys_missing SHAPE -unsorted-p0-nested_call_args CORE_164 unsorted-p0-tag_list_names SHAPE -unsorted-p0-union_no_overlap_flooded RUST_746 -unsorted-p0-union_no_overlap_rev CORE_161 zero-duration-p0-chunk_events_by_key SHAPE -zero-duration-p0-filter_period_intersect RUST_747 -zero-duration-p0-filter_period_intersect_rev RUST_747 -zero-duration-p0-filter_period_intersect_unsorted RUST_747 -zero-duration-p0-flood RUST_746 -zero-duration-p0-flood_pulsetime RUST_746 zero-duration-p0-merge_events_by_keys_app SHAPE zero-duration-p0-merge_events_by_keys_missing SHAPE -zero-duration-p0-nested_call_args CORE_164 zero-duration-p0-tag_list_names SHAPE -zero-duration-p0-union_no_overlap CORE_161,RUST_744 -zero-duration-p0-union_no_overlap_flooded RUST_746 -zero-duration-p0-union_no_overlap_rev CORE_161,RUST_744 zero-inside-b-p0-chunk_events_by_key SHAPE -zero-inside-b-p0-filter_period_intersect RUST_747 -zero-inside-b-p0-filter_period_intersect_rev RUST_747 -zero-inside-b-p0-filter_period_intersect_unsorted RUST_747 -zero-inside-b-p0-flood_pulsetime RUST_746 zero-inside-b-p0-merge_events_by_keys_app SHAPE zero-inside-b-p0-merge_events_by_keys_missing SHAPE -zero-inside-b-p0-nested_call_args CORE_164 zero-inside-b-p0-tag_list_names SHAPE -zero-inside-b-p0-union_no_overlap CORE_161,RUST_744 -zero-inside-b-p0-union_no_overlap_unsorted CORE_161,RUST_744 From e30bfd928df8d1e044348a7df9b67968ba7f787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sat, 26 Sep 2026 23:16:38 +0200 Subject: [PATCH 2/5] build(deps): bump aw-core to 7b24386 and aw-tauri to ec46d16 - aw-core 99cb7ac -> 7b24386: #171 fixes the aw-core test failure that broke the Qt legs (#159 + #169 disagreed on iter_events order), plus #166 and #170. - aw-tauri a2f72df -> ec46d16: ActivityWatch/aw-tauri#271 locks aw-server-rust at e7c5439, matching the release submodule, which scripts/check_tauri_server.py requires (the Tauri legs failed on it). #166 landing fixes the 59 CORE_166 known failures. Re-attributed from runs at the new pins (base and aw-core#165): 473 remain, 22 CORE_165, the rest SHAPE (#1466) and PRECISION. --- aw-core | 2 +- aw-tauri | 2 +- scripts/tests/query_parity/known_failures.txt | 59 ------------------- 3 files changed, 2 insertions(+), 61 deletions(-) diff --git a/aw-core b/aw-core index 99cb7aca7..7b2438663 160000 --- a/aw-core +++ b/aw-core @@ -1 +1 @@ -Subproject commit 99cb7aca7afcb17572e3afc6a2e42eb5827d5622 +Subproject commit 7b243866347f354746fa85720b37cc305f8b51db diff --git a/aw-tauri b/aw-tauri index a2f72df6f..ec46d169f 160000 --- a/aw-tauri +++ b/aw-tauri @@ -1 +1 @@ -Subproject commit a2f72df6f1f7ec321488b06f3ea25a5273349f7d +Subproject commit ec46d169f3950e8f745174557caebde3c002e4ed diff --git a/scripts/tests/query_parity/known_failures.txt b/scripts/tests/query_parity/known_failures.txt index 8db7a33d9..b80757546 100644 --- a/scripts/tests/query_parity/known_failures.txt +++ b/scripts/tests/query_parity/known_failures.txt @@ -127,9 +127,6 @@ period-clip-us-p0-union_no_overlap_unsorted PRECISION python:data-shapes-p0-merge_events_by_keys_app:merge_conserves_app SHAPE python:fractional-p0-union_no_overlap_flooded:no_overlap CORE_165 same-start-p0-chunk_events_by_key SHAPE -same-start-p0-filter_period_intersect CORE_166 -same-start-p0-filter_period_intersect_rev CORE_166 -same-start-p0-filter_period_intersect_unsorted CORE_166 same-start-p0-merge_events_by_keys_app SHAPE same-start-p0-merge_events_by_keys_missing SHAPE same-start-p0-tag_list_names SHAPE @@ -139,9 +136,6 @@ seed00-p0-aw-client:fullDesktopQuery SHAPE seed00-p0-aw-client:fullDesktopQuery+always_active SHAPE seed00-p0-aw-client:fullDesktopQuery+browser SHAPE seed00-p0-chunk_events_by_key SHAPE -seed00-p0-filter_period_intersect CORE_166 -seed00-p0-filter_period_intersect_rev CORE_166 -seed00-p0-filter_period_intersect_unsorted CORE_166 seed00-p0-merge_events_by_keys_app SHAPE seed00-p0-merge_events_by_keys_app_title SHAPE seed00-p0-merge_events_by_keys_missing SHAPE @@ -156,9 +150,6 @@ seed00-p1-aw-client:fullDesktopQuery SHAPE seed00-p1-aw-client:fullDesktopQuery+always_active SHAPE seed00-p1-aw-client:fullDesktopQuery+browser SHAPE seed00-p1-chunk_events_by_key SHAPE -seed00-p1-filter_period_intersect CORE_166 -seed00-p1-filter_period_intersect_rev CORE_166 -seed00-p1-filter_period_intersect_unsorted CORE_166 seed00-p1-merge_events_by_keys_app SHAPE seed00-p1-merge_events_by_keys_app_title SHAPE seed00-p1-merge_events_by_keys_missing SHAPE @@ -172,9 +163,6 @@ seed01-p0-aw-client:fullDesktopQuery SHAPE seed01-p0-aw-client:fullDesktopQuery+always_active SHAPE seed01-p0-aw-client:fullDesktopQuery+browser SHAPE seed01-p0-chunk_events_by_key SHAPE -seed01-p0-filter_period_intersect CORE_166 -seed01-p0-filter_period_intersect_rev CORE_166 -seed01-p0-filter_period_intersect_unsorted CORE_166 seed01-p0-merge_events_by_keys_app SHAPE seed01-p0-merge_events_by_keys_app_title SHAPE seed01-p0-merge_events_by_keys_missing SHAPE @@ -187,9 +175,6 @@ seed01-p1-aw-client:fullDesktopQuery SHAPE seed01-p1-aw-client:fullDesktopQuery+always_active SHAPE seed01-p1-aw-client:fullDesktopQuery+browser SHAPE seed01-p1-chunk_events_by_key SHAPE -seed01-p1-filter_period_intersect CORE_166 -seed01-p1-filter_period_intersect_rev CORE_166 -seed01-p1-filter_period_intersect_unsorted CORE_166 seed01-p1-merge_events_by_keys_app SHAPE seed01-p1-merge_events_by_keys_app_title SHAPE seed01-p1-merge_events_by_keys_missing SHAPE @@ -203,9 +188,6 @@ seed02-p0-aw-client:fullDesktopQuery SHAPE seed02-p0-aw-client:fullDesktopQuery+always_active SHAPE seed02-p0-aw-client:fullDesktopQuery+browser SHAPE seed02-p0-chunk_events_by_key SHAPE -seed02-p0-filter_period_intersect CORE_166 -seed02-p0-filter_period_intersect_rev CORE_166 -seed02-p0-filter_period_intersect_unsorted CORE_166 seed02-p0-merge_events_by_keys_app SHAPE seed02-p0-merge_events_by_keys_app_title SHAPE seed02-p0-merge_events_by_keys_missing SHAPE @@ -220,9 +202,6 @@ seed02-p1-aw-client:fullDesktopQuery SHAPE seed02-p1-aw-client:fullDesktopQuery+always_active SHAPE seed02-p1-aw-client:fullDesktopQuery+browser SHAPE seed02-p1-chunk_events_by_key SHAPE -seed02-p1-filter_period_intersect CORE_166 -seed02-p1-filter_period_intersect_rev CORE_166 -seed02-p1-filter_period_intersect_unsorted CORE_166 seed02-p1-merge_events_by_keys_app SHAPE seed02-p1-merge_events_by_keys_app_title SHAPE seed02-p1-merge_events_by_keys_missing SHAPE @@ -237,8 +216,6 @@ seed03-p0-aw-client:fullDesktopQuery SHAPE seed03-p0-aw-client:fullDesktopQuery+always_active SHAPE seed03-p0-aw-client:fullDesktopQuery+browser SHAPE seed03-p0-chunk_events_by_key SHAPE -seed03-p0-filter_period_intersect CORE_166 -seed03-p0-filter_period_intersect_unsorted CORE_166 seed03-p0-merge_events_by_keys_app SHAPE seed03-p0-merge_events_by_keys_app_title SHAPE seed03-p0-merge_events_by_keys_missing SHAPE @@ -253,8 +230,6 @@ seed03-p1-aw-client:fullDesktopQuery SHAPE seed03-p1-aw-client:fullDesktopQuery+always_active SHAPE seed03-p1-aw-client:fullDesktopQuery+browser SHAPE seed03-p1-chunk_events_by_key SHAPE -seed03-p1-filter_period_intersect CORE_166 -seed03-p1-filter_period_intersect_unsorted CORE_166 seed03-p1-merge_events_by_keys_app SHAPE seed03-p1-merge_events_by_keys_app_title SHAPE seed03-p1-merge_events_by_keys_missing SHAPE @@ -268,8 +243,6 @@ seed04-p0-aw-client:fullDesktopQuery SHAPE seed04-p0-aw-client:fullDesktopQuery+always_active SHAPE seed04-p0-aw-client:fullDesktopQuery+browser SHAPE seed04-p0-chunk_events_by_key SHAPE -seed04-p0-filter_period_intersect CORE_166 -seed04-p0-filter_period_intersect_unsorted CORE_166 seed04-p0-merge_events_by_keys_app SHAPE seed04-p0-merge_events_by_keys_app_title SHAPE seed04-p0-merge_events_by_keys_missing SHAPE @@ -283,8 +256,6 @@ seed04-p1-aw-client:fullDesktopQuery SHAPE seed04-p1-aw-client:fullDesktopQuery+always_active SHAPE seed04-p1-aw-client:fullDesktopQuery+browser SHAPE seed04-p1-chunk_events_by_key SHAPE -seed04-p1-filter_period_intersect CORE_166 -seed04-p1-filter_period_intersect_unsorted CORE_166 seed04-p1-merge_events_by_keys_app SHAPE seed04-p1-merge_events_by_keys_app_title SHAPE seed04-p1-merge_events_by_keys_missing SHAPE @@ -298,7 +269,6 @@ seed05-p0-aw-client:fullDesktopQuery SHAPE seed05-p0-aw-client:fullDesktopQuery+always_active SHAPE seed05-p0-aw-client:fullDesktopQuery+browser SHAPE seed05-p0-chunk_events_by_key SHAPE -seed05-p0-filter_period_intersect_rev CORE_166 seed05-p0-merge_events_by_keys_app SHAPE seed05-p0-merge_events_by_keys_app_title SHAPE seed05-p0-merge_events_by_keys_missing SHAPE @@ -312,7 +282,6 @@ seed05-p1-aw-client:fullDesktopQuery SHAPE seed05-p1-aw-client:fullDesktopQuery+always_active SHAPE seed05-p1-aw-client:fullDesktopQuery+browser SHAPE seed05-p1-chunk_events_by_key SHAPE -seed05-p1-filter_period_intersect_rev CORE_166 seed05-p1-merge_events_by_keys_app SHAPE seed05-p1-merge_events_by_keys_app_title SHAPE seed05-p1-merge_events_by_keys_missing SHAPE @@ -326,7 +295,6 @@ seed06-p0-aw-client:fullDesktopQuery SHAPE seed06-p0-aw-client:fullDesktopQuery+always_active SHAPE seed06-p0-aw-client:fullDesktopQuery+browser SHAPE seed06-p0-chunk_events_by_key SHAPE -seed06-p0-filter_period_intersect_rev CORE_166 seed06-p0-merge_events_by_keys_app SHAPE seed06-p0-merge_events_by_keys_app_title SHAPE seed06-p0-merge_events_by_keys_missing SHAPE @@ -340,7 +308,6 @@ seed06-p1-aw-client:fullDesktopQuery SHAPE seed06-p1-aw-client:fullDesktopQuery+always_active SHAPE seed06-p1-aw-client:fullDesktopQuery+browser SHAPE seed06-p1-chunk_events_by_key SHAPE -seed06-p1-filter_period_intersect_rev CORE_166 seed06-p1-merge_events_by_keys_app SHAPE seed06-p1-merge_events_by_keys_app_title SHAPE seed06-p1-merge_events_by_keys_missing SHAPE @@ -354,9 +321,6 @@ seed07-p0-aw-client:fullDesktopQuery SHAPE seed07-p0-aw-client:fullDesktopQuery+always_active SHAPE seed07-p0-aw-client:fullDesktopQuery+browser SHAPE seed07-p0-chunk_events_by_key SHAPE -seed07-p0-filter_period_intersect CORE_166 -seed07-p0-filter_period_intersect_rev CORE_166 -seed07-p0-filter_period_intersect_unsorted CORE_166 seed07-p0-merge_events_by_keys_app SHAPE seed07-p0-merge_events_by_keys_app_title SHAPE seed07-p0-merge_events_by_keys_missing SHAPE @@ -370,9 +334,6 @@ seed07-p1-aw-client:fullDesktopQuery SHAPE seed07-p1-aw-client:fullDesktopQuery+always_active SHAPE seed07-p1-aw-client:fullDesktopQuery+browser SHAPE seed07-p1-chunk_events_by_key SHAPE -seed07-p1-filter_period_intersect CORE_166 -seed07-p1-filter_period_intersect_rev CORE_166 -seed07-p1-filter_period_intersect_unsorted CORE_166 seed07-p1-merge_events_by_keys_app SHAPE seed07-p1-merge_events_by_keys_app_title SHAPE seed07-p1-merge_events_by_keys_missing SHAPE @@ -387,8 +348,6 @@ seed08-p0-aw-client:fullDesktopQuery SHAPE seed08-p0-aw-client:fullDesktopQuery+always_active SHAPE seed08-p0-aw-client:fullDesktopQuery+browser SHAPE seed08-p0-chunk_events_by_key SHAPE -seed08-p0-filter_period_intersect CORE_166 -seed08-p0-filter_period_intersect_unsorted CORE_166 seed08-p0-merge_events_by_keys_app SHAPE seed08-p0-merge_events_by_keys_app_title SHAPE seed08-p0-merge_events_by_keys_missing SHAPE @@ -416,9 +375,6 @@ seed09-p0-aw-client:fullDesktopQuery SHAPE seed09-p0-aw-client:fullDesktopQuery+always_active SHAPE seed09-p0-aw-client:fullDesktopQuery+browser SHAPE seed09-p0-chunk_events_by_key SHAPE -seed09-p0-filter_period_intersect CORE_166 -seed09-p0-filter_period_intersect_rev CORE_166 -seed09-p0-filter_period_intersect_unsorted CORE_166 seed09-p0-merge_events_by_keys_app SHAPE seed09-p0-merge_events_by_keys_app_title SHAPE seed09-p0-merge_events_by_keys_missing SHAPE @@ -432,9 +388,6 @@ seed09-p1-aw-client:fullDesktopQuery SHAPE seed09-p1-aw-client:fullDesktopQuery+always_active SHAPE seed09-p1-aw-client:fullDesktopQuery+browser SHAPE seed09-p1-chunk_events_by_key SHAPE -seed09-p1-filter_period_intersect CORE_166 -seed09-p1-filter_period_intersect_rev CORE_166 -seed09-p1-filter_period_intersect_unsorted CORE_166 seed09-p1-merge_events_by_keys_app SHAPE seed09-p1-merge_events_by_keys_app_title SHAPE seed09-p1-merge_events_by_keys_missing SHAPE @@ -448,9 +401,6 @@ seed10-p0-aw-client:fullDesktopQuery SHAPE seed10-p0-aw-client:fullDesktopQuery+always_active SHAPE seed10-p0-aw-client:fullDesktopQuery+browser SHAPE seed10-p0-chunk_events_by_key SHAPE -seed10-p0-filter_period_intersect CORE_166 -seed10-p0-filter_period_intersect_rev CORE_166 -seed10-p0-filter_period_intersect_unsorted CORE_166 seed10-p0-merge_events_by_keys_app SHAPE seed10-p0-merge_events_by_keys_app_title SHAPE seed10-p0-merge_events_by_keys_missing SHAPE @@ -464,9 +414,6 @@ seed10-p1-aw-client:fullDesktopQuery SHAPE seed10-p1-aw-client:fullDesktopQuery+always_active SHAPE seed10-p1-aw-client:fullDesktopQuery+browser SHAPE seed10-p1-chunk_events_by_key SHAPE -seed10-p1-filter_period_intersect CORE_166 -seed10-p1-filter_period_intersect_rev CORE_166 -seed10-p1-filter_period_intersect_unsorted CORE_166 seed10-p1-merge_events_by_keys_app SHAPE seed10-p1-merge_events_by_keys_app_title SHAPE seed10-p1-merge_events_by_keys_missing SHAPE @@ -481,9 +428,6 @@ seed11-p0-aw-client:fullDesktopQuery SHAPE seed11-p0-aw-client:fullDesktopQuery+always_active SHAPE seed11-p0-aw-client:fullDesktopQuery+browser SHAPE seed11-p0-chunk_events_by_key SHAPE -seed11-p0-filter_period_intersect CORE_166 -seed11-p0-filter_period_intersect_rev CORE_166 -seed11-p0-filter_period_intersect_unsorted CORE_166 seed11-p0-merge_events_by_keys_app SHAPE seed11-p0-merge_events_by_keys_app_title SHAPE seed11-p0-merge_events_by_keys_missing SHAPE @@ -498,9 +442,6 @@ seed11-p1-aw-client:fullDesktopQuery SHAPE seed11-p1-aw-client:fullDesktopQuery+always_active SHAPE seed11-p1-aw-client:fullDesktopQuery+browser SHAPE seed11-p1-chunk_events_by_key SHAPE -seed11-p1-filter_period_intersect CORE_166 -seed11-p1-filter_period_intersect_rev CORE_166 -seed11-p1-filter_period_intersect_unsorted CORE_166 seed11-p1-merge_events_by_keys_app SHAPE seed11-p1-merge_events_by_keys_app_title SHAPE seed11-p1-merge_events_by_keys_missing SHAPE From 6f7f201b8f6dddc89deacb3983a62be6a556db6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sat, 26 Sep 2026 23:24:13 +0200 Subject: [PATCH 3/5] build(deps): bump aw-core to d6ec34c (aw-core#165) The 22 CORE_165 known failures pass now and none are new. 451 remain, all SHAPE (#1466) and PRECISION: no open parity fixes left. --- aw-core | 2 +- scripts/tests/query_parity/known_failures.txt | 22 ------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/aw-core b/aw-core index 7b2438663..d6ec34c6a 160000 --- a/aw-core +++ b/aw-core @@ -1 +1 @@ -Subproject commit 7b243866347f354746fa85720b37cc305f8b51db +Subproject commit d6ec34c6a2c7d085cc22deaec0071559cdce7661 diff --git a/scripts/tests/query_parity/known_failures.txt b/scripts/tests/query_parity/known_failures.txt index b80757546..f56465186 100644 --- a/scripts/tests/query_parity/known_failures.txt +++ b/scripts/tests/query_parity/known_failures.txt @@ -1,10 +1,7 @@ adjacent-p0-chunk_events_by_key SHAPE -adjacent-p0-flood CORE_165 -adjacent-p0-flood_pulsetime CORE_165 adjacent-p0-merge_events_by_keys_app SHAPE adjacent-p0-merge_events_by_keys_missing SHAPE adjacent-p0-tag_list_names SHAPE -adjacent-p0-union_no_overlap_flooded CORE_165 containment-p0-chunk_events_by_key SHAPE containment-p0-merge_events_by_keys_app SHAPE containment-p0-merge_events_by_keys_missing SHAPE @@ -16,12 +13,9 @@ data-shapes-p0-merge_events_by_keys_missing SHAPE data-shapes-p0-split_url_events SHAPE data-shapes-p0-tag_list_names SHAPE dst-p0-chunk_events_by_key SHAPE -dst-p0-flood CORE_165 -dst-p0-flood_pulsetime CORE_165 dst-p0-merge_events_by_keys_app SHAPE dst-p0-merge_events_by_keys_missing SHAPE dst-p0-tag_list_names SHAPE -dst-p0-union_no_overlap_flooded CORE_165 dst-p1-chunk_events_by_key SHAPE dst-p1-merge_events_by_keys_app SHAPE dst-p1-merge_events_by_keys_missing SHAPE @@ -31,7 +25,6 @@ empty-b-p0-merge_events_by_keys_app SHAPE empty-b-p0-merge_events_by_keys_missing SHAPE empty-b-p0-tag_list_names SHAPE flood-gaps-p0-chunk_events_by_key SHAPE -flood-gaps-p0-flood_pulsetime CORE_165 flood-gaps-p0-merge_events_by_keys_app SHAPE flood-gaps-p0-merge_events_by_keys_missing SHAPE flood-gaps-p0-tag_list_names SHAPE @@ -48,12 +41,9 @@ fpi-zero-at-filter-end-p0-merge_events_by_keys_app SHAPE fpi-zero-at-filter-end-p0-merge_events_by_keys_missing SHAPE fpi-zero-at-filter-end-p0-tag_list_names SHAPE fractional-ms-p0-chunk_events_by_key PRECISION,SHAPE -fractional-ms-p0-flood CORE_165 -fractional-ms-p0-flood_pulsetime CORE_165 fractional-ms-p0-merge_events_by_keys_app PRECISION,SHAPE fractional-ms-p0-merge_events_by_keys_missing PRECISION,SHAPE fractional-ms-p0-tag_list_names PRECISION,SHAPE -fractional-ms-p0-union_no_overlap_flooded CORE_165 fractional-p0-categorize PRECISION fractional-p0-chunk_events_by_key PRECISION,SHAPE fractional-p0-concat PRECISION @@ -125,12 +115,10 @@ period-clip-us-p0-union_no_overlap_flooded PRECISION period-clip-us-p0-union_no_overlap_rev PRECISION period-clip-us-p0-union_no_overlap_unsorted PRECISION python:data-shapes-p0-merge_events_by_keys_app:merge_conserves_app SHAPE -python:fractional-p0-union_no_overlap_flooded:no_overlap CORE_165 same-start-p0-chunk_events_by_key SHAPE same-start-p0-merge_events_by_keys_app SHAPE same-start-p0-merge_events_by_keys_missing SHAPE same-start-p0-tag_list_names SHAPE -seed00-p0-aw-client:canonicalEvents CORE_165 seed00-p0-aw-client:canonicalEvents-android SHAPE seed00-p0-aw-client:fullDesktopQuery SHAPE seed00-p0-aw-client:fullDesktopQuery+always_active SHAPE @@ -144,7 +132,6 @@ seed00-p0-multidevice-webui-android-merge SHAPE seed00-p0-split_url_events SHAPE seed00-p0-tag SHAPE seed00-p0-tag_list_names SHAPE -seed00-p1-aw-client:canonicalEvents CORE_165 seed00-p1-aw-client:canonicalEvents-android SHAPE seed00-p1-aw-client:fullDesktopQuery SHAPE seed00-p1-aw-client:fullDesktopQuery+always_active SHAPE @@ -182,7 +169,6 @@ seed01-p1-multidevice SHAPE seed01-p1-multidevice-webui-android-merge SHAPE seed01-p1-split_url_events SHAPE seed01-p1-tag_list_names SHAPE -seed02-p0-aw-client:canonicalEvents CORE_165 seed02-p0-aw-client:canonicalEvents-android SHAPE seed02-p0-aw-client:fullDesktopQuery SHAPE seed02-p0-aw-client:fullDesktopQuery+always_active SHAPE @@ -196,7 +182,6 @@ seed02-p0-multidevice-webui-android-merge SHAPE seed02-p0-split_url_events SHAPE seed02-p0-tag SHAPE seed02-p0-tag_list_names SHAPE -seed02-p1-aw-client:canonicalEvents CORE_165 seed02-p1-aw-client:canonicalEvents-android SHAPE seed02-p1-aw-client:fullDesktopQuery SHAPE seed02-p1-aw-client:fullDesktopQuery+always_active SHAPE @@ -210,7 +195,6 @@ seed02-p1-multidevice-webui-android-merge SHAPE seed02-p1-split_url_events SHAPE seed02-p1-tag SHAPE seed02-p1-tag_list_names SHAPE -seed03-p0-aw-client:canonicalEvents CORE_165 seed03-p0-aw-client:canonicalEvents-android SHAPE seed03-p0-aw-client:fullDesktopQuery SHAPE seed03-p0-aw-client:fullDesktopQuery+always_active SHAPE @@ -224,7 +208,6 @@ seed03-p0-multidevice-webui-android-merge SHAPE seed03-p0-split_url_events SHAPE seed03-p0-tag SHAPE seed03-p0-tag_list_names SHAPE -seed03-p1-aw-client:canonicalEvents CORE_165 seed03-p1-aw-client:canonicalEvents-android SHAPE seed03-p1-aw-client:fullDesktopQuery SHAPE seed03-p1-aw-client:fullDesktopQuery+always_active SHAPE @@ -342,7 +325,6 @@ seed07-p1-multidevice-webui-android-merge SHAPE seed07-p1-split_url_events SHAPE seed07-p1-tag SHAPE seed07-p1-tag_list_names SHAPE -seed08-p0-aw-client:canonicalEvents CORE_165 seed08-p0-aw-client:canonicalEvents-android SHAPE seed08-p0-aw-client:fullDesktopQuery SHAPE seed08-p0-aw-client:fullDesktopQuery+always_active SHAPE @@ -356,7 +338,6 @@ seed08-p0-multidevice-webui-android-merge SHAPE seed08-p0-split_url_events SHAPE seed08-p0-tag SHAPE seed08-p0-tag_list_names SHAPE -seed08-p1-aw-client:canonicalEvents CORE_165 seed08-p1-aw-client:canonicalEvents-android SHAPE seed08-p1-aw-client:fullDesktopQuery SHAPE seed08-p1-aw-client:fullDesktopQuery+always_active SHAPE @@ -422,7 +403,6 @@ seed10-p1-multidevice-webui-android-merge SHAPE seed10-p1-split_url_events SHAPE seed10-p1-tag SHAPE seed10-p1-tag_list_names SHAPE -seed11-p0-aw-client:canonicalEvents CORE_165 seed11-p0-aw-client:canonicalEvents-android SHAPE seed11-p0-aw-client:fullDesktopQuery SHAPE seed11-p0-aw-client:fullDesktopQuery+always_active SHAPE @@ -436,7 +416,6 @@ seed11-p0-multidevice-webui-android-merge SHAPE seed11-p0-split_url_events SHAPE seed11-p0-tag SHAPE seed11-p0-tag_list_names SHAPE -seed11-p1-aw-client:canonicalEvents CORE_165 seed11-p1-aw-client:canonicalEvents-android SHAPE seed11-p1-aw-client:fullDesktopQuery SHAPE seed11-p1-aw-client:fullDesktopQuery+always_active SHAPE @@ -459,7 +438,6 @@ straddle-p0-merge_events_by_keys_app SHAPE straddle-p0-merge_events_by_keys_missing SHAPE straddle-p0-tag_list_names SHAPE unsorted-p0-chunk_events_by_key SHAPE -unsorted-p0-flood_pulsetime CORE_165 unsorted-p0-merge_events_by_keys_app SHAPE unsorted-p0-merge_events_by_keys_missing SHAPE unsorted-p0-tag_list_names SHAPE From 28be777cc64bdbf497fe2f43febe63b599030eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 27 Sep 2026 00:03:23 +0200 Subject: [PATCH 4/5] fix(research): sanitize JSON exports before headers and disable CSV export aw-server-rust#721 moved JSON export serialization onto a background thread that runs after the 200 headers, so the sanitizer anchor from #1449 no longer exists (the research legs failed closed on it), and a sanitizer failure there could no longer become an error response. aw-server-rust#722 added a raw-event CSV export that no sanitizer covers. In Research Edition builds (approved by Erik, options A + B): - A: BucketsExportRocket::new exports, sanitizes and re-spools to a tempfile before any headers, so a refused export is still a fail-closed 409; respond_to only streams the sanitized spool. - B: BucketEventsCsvRocket::new returns 403 "CSV export is disabled in Research Edition". The patcher checks every anchor (export struct, export impl, CSV, mod.rs) for exactly one match before writing anything, so a missing or duplicated anchor fails the build without leaving a half-patched tree. Tempfiles are unnamed (tempfile::tempfile), so they are removed on every path. Verified on a patched aw-server-rust e7c5439 release build: a clean currentwindow bucket exports with 200 and the hostname rewritten to research-participant (the real one appears nowhere), an event with a url makes /api/0/export and /api/0/buckets//export return 409, and /api/0/buckets//export/csv returns 403. --- scripts/patch_research_edition_export.py | 277 +++++++++++++----- .../test_patch_research_edition_export.py | 173 +++++++---- 2 files changed, 329 insertions(+), 121 deletions(-) diff --git a/scripts/patch_research_edition_export.py b/scripts/patch_research_edition_export.py index 8d685c5e1..4d32ac74f 100644 --- a/scripts/patch_research_edition_export.py +++ b/scripts/patch_research_edition_export.py @@ -1,79 +1,215 @@ #!/usr/bin/env python3 -"""Patch aw-server-rust's export path for the Research Edition build. +"""Patch aw-server-rust's export endpoints for the Research Edition build. Run as part of the CI build for research edition: python3 scripts/patch_research_edition_export.py [repo-root] -Standard builds never run this script, so `/api/0/export` stays byte-for-byte -unchanged outside Research Edition. The patch is fail-closed: missing markers -abort the build rather than shipping an unsanitized artifact. - -Where the sanitizer hooks in: since aw-server-rust#677 the export is streamed -by `aw_datastore::export_to_file` with bounded event buffering, and both -`/api/0/export` and `/api/0/buckets//export` are one-line calls to -`BucketsExportRocket::new` in `endpoints/util.rs`. No whole `BucketsExport` -value exists at the endpoints any more, so the sanitizer — which needs the -whole export to fail closed on unfiltered events and to detect identity -collisions across buckets — is spliced into `BucketsExportRocket::new`: the -spooled JSON is re-read, sanitized, and spooled again. Research exports are -category-only, so this relaxes the streaming memory bound only where the -data is already small. +Standard builds never run this script, so the export endpoints stay unchanged +outside Research Edition. The patch is fail-closed: every anchor below must +match exactly once (or already be patched), otherwise the build aborts rather +than shipping an unsanitized artifact. + +What it does (ActivityWatch/activitywatch#1449 anchored the sanitizer the last +time; this follows aw-server-rust#721 and #722): + +A. JSON export (``/api/0/export``, ``/api/0/buckets//export``). Since + aw-server-rust#721 the export is serialized on a background thread after the + 200 headers are sent, so a sanitizer failure there could no longer turn + into an error response. In research builds ``BucketsExportRocket::new`` + instead spools the export, sanitizes it and spools the result again, all + before any headers, so a failure is still a fail-closed 409 (500 for I/O + errors), and ``respond_to`` only streams the sanitized tempfile. The + sanitizer needs the whole export (fail closed on unfiltered events, identity + collisions across buckets); research exports are category-only, so this + gives up stream-after-headers only where the data is already small. +B. CSV export (``/api/0/buckets//export/csv``, aw-server-rust#722) streams + raw events and has no sanitizer, so research builds disable it: + ``BucketEventsCsvRocket::new`` returns 403. + +Tempfiles come from ``tempfile::tempfile()``, which are unnamed (unlinked on +Unix, delete-on-close on Windows), so they are removed on every path, including +errors, as soon as the handle is dropped. """ from __future__ import annotations import pathlib import shutil import sys +from typing import List, Tuple +# One marker per anchor, so a partially patched tree is detected. MARKER = "RESEARCH_EDITION_EXPORT_SANITIZE" +STRUCT_MARKER = "RESEARCH_EDITION_EXPORT_STRUCT" +CSV_MARKER = "RESEARCH_EDITION_CSV_EXPORT_DISABLED" + +# A: the export struct gains the sanitized spool. +STRUCT_NEEDLE = """pub struct BucketsExportRocket { + datastore: aw_datastore::Datastore, + bucket_id: Option, + filename: String, +} +""" -# The two lines in BucketsExportRocket::new that spool the export and rewind -# it. The sanitizer is inserted right after them and shadows `file`. -EXPORT_INSERT_NEEDLE = """ let (mut file, name) = datastore.export_to_file(bucket_id, file)?; - file.seek(SeekFrom::Start(0)).map_err(io_error)?; +STRUCT_REPLACEMENT = f"""// {STRUCT_MARKER}: research builds serve a sanitized spool built before +// headers; the streaming fields are kept but unused. +#[allow(dead_code)] +pub struct BucketsExportRocket {{ + datastore: aw_datastore::Datastore, + bucket_id: Option, + filename: String, + sanitized: File, +}} + +/// Export, sanitize and re-spool, before any headers are sent, so a failure is +/// an error response (409 when the sanitizer refuses) instead of a 200 with a +/// truncated body. Both tempfiles are unnamed and removed when dropped. +fn research_sanitized_export( + datastore: &aw_datastore::Datastore, + bucket_id: Option<&str>, +) -> Result {{ + let io_error = |err: std::io::Error| {{ + error!("Failed to prepare sanitized export: {{err}}"); + HttpErrorJson::new( + Status::InternalServerError, + "Failed to prepare export file".into(), + ) + }}; + let staging = tempfile::tempfile().map_err(io_error)?; + let (mut staging, _) = datastore.export_to_file(bucket_id, staging)?; + staging.seek(SeekFrom::Start(0)).map_err(io_error)?; + let export: aw_models::BucketsExport = + serde_json::from_reader(std::io::BufReader::new(&staging)).map_err(|err| {{ + error!("Failed to parse export for sanitizing: {{err}}"); + HttpErrorJson::new( + Status::InternalServerError, + "Failed to prepare export file".into(), + ) + }})?; + drop(staging); + let export = super::export_sanitize::sanitize_buckets_export(export) + .map_err(|err| HttpErrorJson::new(Status::Conflict, err))?; + let mut sanitized = tempfile::tempfile().map_err(io_error)?; + {{ + let mut writer = std::io::BufWriter::new(&mut sanitized); + serde_json::to_writer(&mut writer, &export).map_err(|err| {{ + error!("Failed to write sanitized export: {{err}}"); + HttpErrorJson::new( + Status::InternalServerError, + "Failed to prepare export file".into(), + ) + }})?; + std::io::Write::flush(&mut writer).map_err(io_error)?; + }} + sanitized.seek(SeekFrom::Start(0)).map_err(io_error)?; + Ok(sanitized) +}} """ -EXPORT_INSERT_REPLACEMENT = f""" let (mut file, name) = datastore.export_to_file(bucket_id, file)?; - file.seek(SeekFrom::Start(0)).map_err(io_error)?; - // {MARKER} - // The datastore streams the export with bounded event buffering, so no - // whole `BucketsExport` exists here. The Research Edition sanitizer - // needs one (fail closed on unfiltered events, identity rewriting with - // collision detection across buckets): re-read the spooled JSON, - // sanitize, and spool again. Research exports are category-only, so - // this relaxes the memory bound only where the data is already small. - let file = {{ - let export: aw_models::BucketsExport = - serde_json::from_reader(std::io::BufReader::new(&file)).map_err(|err| {{ - error!("Failed to parse export for sanitizing: {{err}}"); - HttpErrorJson::new( - Status::InternalServerError, - "Failed to prepare export file".into(), - ) - }})?; - let export = super::export_sanitize::sanitize_buckets_export(export) - .map_err(|err| HttpErrorJson::new(Status::Conflict, err))?; - let mut sanitized = tempfile::tempfile().map_err(io_error)?; - {{ - let mut writer = std::io::BufWriter::new(&mut sanitized); - serde_json::to_writer(&mut writer, &export).map_err(|err| {{ - error!("Failed to write sanitized export: {{err}}"); - HttpErrorJson::new( - Status::InternalServerError, - "Failed to prepare export file".into(), - ) - }})?; - std::io::Write::flush(&mut writer).map_err(io_error)?; - }} - sanitized.seek(SeekFrom::Start(0)).map_err(io_error)?; - sanitized - }}; +# A: BucketsExportRocket::new and its Responder, as of aw-server-rust#721. +EXPORT_INSERT_NEEDLE = """impl BucketsExportRocket { + pub fn new( + datastore: &aw_datastore::Datastore, + bucket_id: Option<&str>, + ) -> Result { + // Resolve the download name and 404 missing buckets before the + // response is built. Serialization itself runs after headers so a + // slow export does not look like a hung connection. + let filename = export_filename(datastore, bucket_id)?; + Ok(Self { + datastore: datastore.clone(), + bucket_id: bucket_id.map(str::to_owned), + filename, + }) + } +} + +impl<'r> Responder<'r, 'static> for BucketsExportRocket { + fn respond_to(self, _: &Request) -> response::Result<'static> { + let (reader, writer) = pipe().map_err(|err| { + error!("Failed to open export pipe: {err}"); + Status::InternalServerError + })?; + spawn_export_stream(self.datastore, self.bucket_id, writer); + Response::build() + .status(Status::Ok) + .header(Header::new("Content-Disposition", self.filename)) + .header(ContentType::JSON) + .streamed_body(rocket::tokio::fs::File::from_std(pipe_reader_to_file( + reader, + ))) + .ok() + } +} +""" + +EXPORT_INSERT_REPLACEMENT = f"""impl BucketsExportRocket {{ + pub fn new( + datastore: &aw_datastore::Datastore, + bucket_id: Option<&str>, + ) -> Result {{ + let filename = export_filename(datastore, bucket_id)?; + // {MARKER}: sanitize before headers so failures stay fail-closed. + let sanitized = research_sanitized_export(datastore, bucket_id)?; + Ok(Self {{ + datastore: datastore.clone(), + bucket_id: bucket_id.map(str::to_owned), + filename, + sanitized, + }}) + }} +}} + +impl<'r> Responder<'r, 'static> for BucketsExportRocket {{ + fn respond_to(self, _: &Request) -> response::Result<'static> {{ + // Research Edition: only the sanitized spool built in `new` is sent. + Response::build() + .status(Status::Ok) + .header(Header::new("Content-Disposition", self.filename)) + .header(ContentType::JSON) + .streamed_body(rocket::tokio::fs::File::from_std(self.sanitized)) + .ok() + }} +}} +""" + +# B: the CSV export (aw-server-rust#722) streams raw events; disable it. +CSV_NEEDLE = """impl BucketEventsCsvRocket { + pub fn new( + datastore: &aw_datastore::Datastore, + bucket_id: &str, + start: Option>, + end: Option>, + limit: Option, + ) -> Result { +""" + +CSV_REPLACEMENT = f"""impl BucketEventsCsvRocket {{ + #[allow(unreachable_code, unused_variables)] + pub fn new( + datastore: &aw_datastore::Datastore, + bucket_id: &str, + start: Option>, + end: Option>, + limit: Option, + ) -> Result {{ + // {CSV_MARKER}: raw-event CSV export has no sanitizer. + return Err(HttpErrorJson::new( + Status::Forbidden, + "CSV export is disabled in Research Edition".into(), + )); """ MOD_NEEDLE = "mod export;\n" MOD_REPLACEMENT = "mod export;\nmod export_sanitize;\n" +# (name, needle, replacement, marker) +UTIL_EDITS: List[Tuple[str, str, str, str]] = [ + ("export-struct", STRUCT_NEEDLE, STRUCT_REPLACEMENT, STRUCT_MARKER), + ("export-sanitizer", EXPORT_INSERT_NEEDLE, EXPORT_INSERT_REPLACEMENT, MARKER), + ("csv-export", CSV_NEEDLE, CSV_REPLACEMENT, CSV_MARKER), +] + def repo_root_from_args(argv: list[str]) -> pathlib.Path: if len(argv) > 1: @@ -81,16 +217,19 @@ def repo_root_from_args(argv: list[str]) -> pathlib.Path: return pathlib.Path.cwd().resolve() -def _replace_once(path: pathlib.Path, needle: str, replacement: str, already_ok: str) -> None: +def _edited_text(path: pathlib.Path, edits: List[Tuple[str, str, str, str]]) -> str: + """The patched text of ``path``; raises if any anchor isn't found exactly once.""" text = path.read_text(encoding="utf-8") - if already_ok in text: - return - count = text.count(needle) - if count != 1: - raise ValueError( - f"{path}: expected exactly one export-sanitizer insertion point, found {count}" - ) - path.write_text(text.replace(needle, replacement, 1), encoding="utf-8") + for name, needle, replacement, marker in edits: + if marker in text: + continue # already patched + count = text.count(needle) + if count != 1: + raise ValueError( + f"{path}: expected exactly one {name} insertion point, found {count}" + ) + text = text.replace(needle, replacement, 1) + return text def patch_tree(repo_root: pathlib.Path) -> None: @@ -110,10 +249,16 @@ def patch_tree(repo_root: pathlib.Path) -> None: if not required.is_file(): raise FileNotFoundError(f"expected Rust export source at {required}") + # Check every anchor before writing anything, so a missing one leaves the + # tree untouched and the build fails. + util_text = _edited_text(util_rs, UTIL_EDITS) + mod_text = _edited_text( + mod_rs, + [("export-sanitize module", MOD_NEEDLE, MOD_REPLACEMENT, "mod export_sanitize;")], + ) shutil.copyfile(source, dest) - - _replace_once(mod_rs, MOD_NEEDLE, MOD_REPLACEMENT, "mod export_sanitize;") - _replace_once(util_rs, EXPORT_INSERT_NEEDLE, EXPORT_INSERT_REPLACEMENT, MARKER) + util_rs.write_text(util_text, encoding="utf-8") + mod_rs.write_text(mod_text, encoding="utf-8") def main() -> None: diff --git a/scripts/tests/test_patch_research_edition_export.py b/scripts/tests/test_patch_research_edition_export.py index 0f1d5bfb4..a49491c58 100644 --- a/scripts/tests/test_patch_research_edition_export.py +++ b/scripts/tests/test_patch_research_edition_export.py @@ -26,58 +26,93 @@ def _write_tree(tmp_path: Path, util: str, mod: str) -> Path: return tmp_path -# Shape of BucketsExportRocket::new after aw-server-rust#677: the export is -# spooled to a tempfile by the datastore and rewound. Both export endpoints -# go through this one function, so it is the single insertion point. -UTIL_SRC = """impl BucketsExportRocket { - pub fn new( - datastore: &aw_datastore::Datastore, - bucket_id: Option<&str>, - ) -> Result { - let file = tempfile::tempfile().map_err(io_error)?; - let (mut file, name) = datastore.export_to_file(bucket_id, file)?; - file.seek(SeekFrom::Start(0)).map_err(io_error)?; - let filename = match name { - Some(id) => format!("attachment; filename=aw-bucket-export_{id}.json"), - None => "attachment; filename=aw-buckets-export.json".into(), - }; - Ok(Self { file, filename }) - } -} -""" +# util.rs as of aw-server-rust#721/#722: the export struct, the streaming +# BucketsExportRocket, and the CSV export. Built from the patcher's own anchors +# so the fixture tracks them; test_live_tree_* checks them against the real tree. +UTIL_SRC = ( + "use std::fs::File;\n\n" + + patcher.STRUCT_NEEDLE + + "\nfn export_filename() {}\n\n" + + patcher.EXPORT_INSERT_NEEDLE + + "\n// ── CSV streaming export\n\n" + + patcher.CSV_NEEDLE + + " datastore.get_bucket(bucket_id)?;\n }\n}\n" +) MOD_SRC = """mod util; mod export; mod hostcheck; """ +ENDPOINTS = "aw-server-rust/aw-server/src/endpoints" + def _util(root: Path) -> str: - return (root / "aw-server-rust/aw-server/src/endpoints/util.rs").read_text( - encoding="utf-8" - ) + return (root / ENDPOINTS / "util.rs").read_text(encoding="utf-8") + + +def _mod(root: Path) -> str: + return (root / ENDPOINTS / "mod.rs").read_text(encoding="utf-8") + + +def _fn_body(text: str, signature: str) -> str: + start = text.index(signature) + end = text.index("\n}\n", start) + return text[start:end] -def test_patch_inserts_module_and_call_site(tmp_path: Path): +def test_patch_inserts_module_and_all_anchors(tmp_path: Path): root = _write_tree(tmp_path, UTIL_SRC, MOD_SRC) patcher.patch_tree(root) util = _util(root) - mod = (root / "aw-server-rust/aw-server/src/endpoints/mod.rs").read_text( - encoding="utf-8" - ) - copied = root / "aw-server-rust/aw-server/src/endpoints/export_sanitize.rs" + assert (root / ENDPOINTS / "export_sanitize.rs").is_file() + assert "mod export_sanitize;" in _mod(root) + for marker in (patcher.MARKER, patcher.STRUCT_MARKER, patcher.CSV_MARKER): + assert util.count(marker) == 1, marker + # None of the original streaming export code paths survive + assert patcher.EXPORT_INSERT_NEEDLE not in util + assert "spawn_export_stream(self.datastore" not in util + + +def test_json_export_is_sanitized_before_headers_with_409(tmp_path: Path): + root = _write_tree(tmp_path, UTIL_SRC, MOD_SRC) + patcher.patch_tree(root) + util = _util(root) + + helper = _fn_body(util, "fn research_sanitized_export(") + # The sanitizer's refusal is a 409, other failures are errors too. + assert "sanitize_buckets_export(export)" in helper + assert "Status::Conflict" in helper + assert "Status::InternalServerError" in helper + # Unnamed tempfiles only (removed when dropped, on every path) + assert helper.count("tempfile::tempfile()") == 2 + assert "NamedTempFile" not in util - assert copied.is_file() - assert "mod export_sanitize;" in mod - assert util.count(patcher.MARKER) == 1 - assert "sanitize_buckets_export" in util - assert "Status::Conflict" in util - # The sanitized spool replaces `file` before the filename is chosen and - # the struct is built, so the response body is the sanitized JSON. - assert util.index(patcher.MARKER) < util.index("let filename = match name") - assert "sanitized.seek(SeekFrom::Start(0))" in util + # `new` runs it (before any response exists) and `?` returns its error + new = _fn_body(util, "impl BucketsExportRocket {") + assert "research_sanitized_export(datastore, bucket_id)?" in new + assert new.index("research_sanitized_export") < new.index("Ok(Self {") + + # The responder only streams the sanitized spool + responder = _fn_body(util, "impl<'r> Responder<'r, 'static> for BucketsExportRocket") + assert "from_std(self.sanitized)" in responder + assert "export_to_file" not in responder + assert "spawn_export_stream" not in responder + + +def test_csv_export_returns_403(tmp_path: Path): + root = _write_tree(tmp_path, UTIL_SRC, MOD_SRC) + patcher.patch_tree(root) + new = _fn_body(_util(root), "impl BucketEventsCsvRocket {") + body = new.split(") -> Result {", 1)[1] + # The very first statement refuses; nothing touches the datastore before it + first = body.strip().splitlines() + assert first[0].startswith("// " + patcher.CSV_MARKER) + assert first[1].strip() == "return Err(HttpErrorJson::new(" + assert "Status::Forbidden" in body.split("));", 1)[0] + assert "CSV export is disabled in Research Edition" in body def test_patch_is_idempotent(tmp_path: Path): @@ -85,24 +120,51 @@ def test_patch_is_idempotent(tmp_path: Path): patcher.patch_tree(root) first = _util(root) patcher.patch_tree(root) - second = _util(root) - assert first == second - mod = (root / "aw-server-rust/aw-server/src/endpoints/mod.rs").read_text( - encoding="utf-8" - ) - assert mod.count("mod export_sanitize;") == 1 + assert _util(root) == first + assert _mod(root).count("mod export_sanitize;") == 1 + +@pytest.mark.parametrize( + "anchor,name", + [ + ("STRUCT_NEEDLE", "export-struct"), + ("EXPORT_INSERT_NEEDLE", "export-sanitizer"), + ("CSV_NEEDLE", "csv-export"), + ], +) +def test_patch_fails_closed_without_any_one_anchor(tmp_path: Path, anchor, name): + util = UTIL_SRC.replace(getattr(patcher, anchor), "// changed upstream\n") + root = _write_tree(tmp_path, util, MOD_SRC) + with pytest.raises(ValueError, match=f"exactly one {name} insertion point, found 0"): + patcher.patch_tree(root) + # Nothing was written: no half-patched tree + assert _util(root) == util + assert _mod(root) == MOD_SRC + assert not (root / ENDPOINTS / "export_sanitize.rs").exists() -def test_patch_fails_closed_without_export_marker(tmp_path: Path): - root = _write_tree(tmp_path, "impl BucketsExportRocket {}\n", MOD_SRC) - with pytest.raises(ValueError, match="insertion point"): + +def test_patch_fails_closed_on_duplicate_anchor(tmp_path: Path): + util = UTIL_SRC + patcher.CSV_NEEDLE + root = _write_tree(tmp_path, util, MOD_SRC) + with pytest.raises(ValueError, match="csv-export insertion point, found 2"): patcher.patch_tree(root) -def test_patch_fails_closed_on_pre_677_endpoints(tmp_path: Path): - # A tree where the endpoints still build `export.buckets` themselves has - # no spooling call site; the patch must refuse rather than ship unsanitized. - legacy_util = "impl BucketsExportRocket {\n pub fn new() {}\n}\n" +def test_patch_fails_closed_without_mod_anchor(tmp_path: Path): + root = _write_tree(tmp_path, UTIL_SRC, "mod util;\n") + with pytest.raises(ValueError, match="export-sanitize module insertion point"): + patcher.patch_tree(root) + assert _util(root) == UTIL_SRC + + +def test_patch_fails_closed_on_pre_721_endpoints(tmp_path: Path): + # The #1449 shape (sanitizer spliced after export_to_file in `new`) is gone + # upstream; a tree still shaped like that must not be half patched. + legacy_util = ( + "impl BucketsExportRocket {\n pub fn new() {\n" + " let (mut file, name) = datastore.export_to_file(bucket_id, file)?;\n" + " }\n}\n" + ) root = _write_tree(tmp_path, legacy_util, MOD_SRC) with pytest.raises(ValueError, match="found 0"): patcher.patch_tree(root) @@ -110,14 +172,15 @@ def test_patch_fails_closed_on_pre_677_endpoints(tmp_path: Path): def test_live_tree_is_patchable_or_already_patched(): root = Path(__file__).resolve().parents[2] - util = root / "aw-server-rust/aw-server/src/endpoints/util.rs" + util = root / ENDPOINTS / "util.rs" if not util.is_file(): pytest.skip("aw-server-rust not checked out") - util_text = util.read_text(encoding="utf-8") - assert ( - patcher.MARKER in util_text - or util_text.count(patcher.EXPORT_INSERT_NEEDLE) == 1 - ), "BucketsExportRocket::new no longer matches the research export patch; update EXPORT_INSERT_NEEDLE" + text = util.read_text(encoding="utf-8") + for name, needle, _, marker in patcher.UTIL_EDITS: + assert marker in text or text.count(needle) == 1, ( + f"aw-server-rust no longer matches the research export patch ({name}); " + "update scripts/patch_research_edition_export.py" + ) def test_sanitizer_allowlist_covers_config_categories(): From 58451bc44f2d007418c4ab58ac6e2bb55d0c5c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 27 Sep 2026 00:23:34 +0200 Subject: [PATCH 5/5] fix(research): assert the research export behaviour in upstream's tests The research legs run aw-server-rust's full test suite against the patched server, and two tests new since the bump assert the standard behaviour: aw-server's CSV export test (now 403 by design) and aw-client-rust's export/import round trip (#727; research exports rewrite the bucket id and hostname). The patcher now rewrites both in research builds to assert the research behaviour instead: CSV export is 403 for existing and missing buckets, and exports contain no real hostname or bucket id. Same exactly-one-anchor, all-or-nothing rules as the server edits. `cargo test --no-fail-fast` on a patched aw-server-rust e7c5439: all pass. --- scripts/patch_research_edition_export.py | 121 ++++++++++++++++++ .../test_patch_research_edition_export.py | 64 +++++++-- 2 files changed, 177 insertions(+), 8 deletions(-) diff --git a/scripts/patch_research_edition_export.py b/scripts/patch_research_edition_export.py index 4d32ac74f..884e651f9 100644 --- a/scripts/patch_research_edition_export.py +++ b/scripts/patch_research_edition_export.py @@ -27,6 +27,11 @@ raw events and has no sanitizer, so research builds disable it: ``BucketEventsCsvRocket::new`` returns 403. +The research legs run the full aw-server-rust test suite, so two upstream tests +that assert the standard behaviour (the CSV export test in aw-server, and the +export round trip in aw-client-rust) are rewritten to assert the research +behaviour: CSV export is 403, and exports contain no real hostname or bucket id. + Tempfiles come from ``tempfile::tempfile()``, which are unnamed (unlinked on Unix, delete-on-close on Windows), so they are removed on every path, including errors, as soon as the handle is dropped. @@ -211,6 +216,113 @@ ] +# Upstream tests that assert the standard export behaviour, rewritten for +# research builds to assert the research behaviour instead (the research legs +# run the full aw-server-rust test suite against the patched server). +CSV_TEST_MARKER = "RESEARCH_EDITION_CSV_EXPORT_TEST" +CSV_TEST_NEEDLE = r""" let client = Client::untracked(server).unwrap(); + let response = client + .get("/api/0/buckets/testbucket/export/csv") + .header(Header::new("Host", "127.0.0.1:5600")) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + assert_eq!( + response.content_type(), + Some(ContentType::new("text", "csv")) + ); + assert_eq!( + response.headers().get_one("Content-Disposition"), + Some("attachment; filename=\"aw-events-export-testbucket.csv\"") + ); + let body = response.into_string().unwrap(); + // Header row + assert!(body.starts_with("id,timestamp,duration,"), "header: {body}"); + // Quoted field for title with embedded double-quote + assert!( + body.contains("\"A \"\"quoted\"\" title\""), + "quoting: {body}" + ); + // Sub-millisecond duration is not truncated to 0.001000000 + assert!(body.contains("0.001500000"), "duration: {body}"); + // Spreadsheet formula prefixes are neutralized + assert!(body.contains("'=cmd|calc"), "formula: {body}"); + // Event id present + let event_id = inserted[0].id.unwrap().to_string(); + assert!(body.contains(&event_id), "id in body: {body}"); + + // Missing bucket → 404 with JSON body + let response = client + .get("/api/0/buckets/nosuchbucket/export/csv") + .header(Header::new("Host", "127.0.0.1:5600")) + .dispatch(); + assert_eq!(response.status(), Status::NotFound); + let body: Value = serde_json::from_str(&response.into_string().unwrap()).unwrap(); + assert!(body["message"].as_str().unwrap().contains("does not exist")); +""" + +CSV_TEST_REPLACEMENT = f""" let _ = inserted; + // {CSV_TEST_MARKER}: CSV export is disabled in research builds, for + // existing and missing buckets alike. + let client = Client::untracked(server).unwrap(); + for bucket in ["testbucket", "nosuchbucket"] {{ + let response = client + .get(format!("/api/0/buckets/{{bucket}}/export/csv")) + .header(Header::new("Host", "127.0.0.1:5600")) + .dispatch(); + assert_eq!(response.status(), Status::Forbidden); + let body: Value = serde_json::from_str(&response.into_string().unwrap()).unwrap(); + assert_eq!(body["message"], "CSV export is disabled in Research Edition"); + }} +""" + +CLIENT_TEST_MARKER = "RESEARCH_EDITION_CLIENT_EXPORT_TEST" +CLIENT_TEST_NEEDLE = r""" let all = client.export_all().unwrap(); + assert!(all.buckets.contains_key(&bucketname)); + let export = client.export_bucket(&bucketname).unwrap(); + let exported = export.buckets[&bucketname].clone(); + assert_eq!(exported.events.clone().unwrap().take_inner().len(), 1); + + client.delete_bucket(&bucketname).unwrap(); + client.import_bucket(&exported).unwrap(); + let reimported = client.get_events(&bucketname, None, None, None).unwrap(); + assert_eq!(reimported.len(), 1); + assert_eq!(reimported[0].timestamp, event.timestamp); + assert_eq!(reimported[0].duration, event.duration); + assert_eq!(reimported[0].data, event.data); +""" + +CLIENT_TEST_REPLACEMENT = f""" // {CLIENT_TEST_MARKER}: research exports rewrite bucket identities and + // hostnames, so the real hostname must not appear, and a sanitized + // export isn't meant to round-trip into the same database. + let all = client.export_all().unwrap(); + assert!(!all.buckets.contains_key(&bucketname)); + assert!(all + .buckets + .values() + .all(|bucket| bucket.hostname == "research-participant")); + if !client.hostname.is_empty() {{ + assert!(all.buckets.keys().all(|key| !key.contains(&client.hostname))); + }} + let export = client.export_bucket(&bucketname).unwrap(); + assert_eq!(export.buckets.len(), 1); + assert!(!export.buckets.contains_key(&bucketname)); + let exported = export.buckets.values().next().unwrap(); + assert_eq!(exported.events.clone().unwrap().take_inner().len(), 1); +""" + +# (relative path, [(name, needle, replacement, marker)]) +TEST_EDITS: List[Tuple[str, List[Tuple[str, str, str, str]]]] = [ + ( + "aw-server/tests/api.rs", + [("csv-export test", CSV_TEST_NEEDLE, CSV_TEST_REPLACEMENT, CSV_TEST_MARKER)], + ), + ( + "aw-client-rust/tests/test.rs", + [("client export test", CLIENT_TEST_NEEDLE, CLIENT_TEST_REPLACEMENT, CLIENT_TEST_MARKER)], + ), +] + + def repo_root_from_args(argv: list[str]) -> pathlib.Path: if len(argv) > 1: return pathlib.Path(argv[1]).resolve() @@ -256,9 +368,18 @@ def patch_tree(repo_root: pathlib.Path) -> None: mod_rs, [("export-sanitize module", MOD_NEEDLE, MOD_REPLACEMENT, "mod export_sanitize;")], ) + server_rust = repo_root / "aw-server-rust" + test_texts = [] + for relative, edits in TEST_EDITS: + path = server_rust / relative + if not path.is_file(): + raise FileNotFoundError(f"expected Rust test source at {path}") + test_texts.append((path, _edited_text(path, edits))) shutil.copyfile(source, dest) util_rs.write_text(util_text, encoding="utf-8") mod_rs.write_text(mod_text, encoding="utf-8") + for path, text in test_texts: + path.write_text(text, encoding="utf-8") def main() -> None: diff --git a/scripts/tests/test_patch_research_edition_export.py b/scripts/tests/test_patch_research_edition_export.py index a49491c58..d9724abf1 100644 --- a/scripts/tests/test_patch_research_edition_export.py +++ b/scripts/tests/test_patch_research_edition_export.py @@ -18,11 +18,30 @@ CONFIG_SPEC.loader.exec_module(config_patcher) -def _write_tree(tmp_path: Path, util: str, mod: str) -> Path: - endpoints = tmp_path / "aw-server-rust" / "aw-server" / "src" / "endpoints" +API_TEST_SRC = "fn csv_test() {\n" + patcher.CSV_TEST_NEEDLE + " }\n" +CLIENT_TEST_SRC = "fn test_full() {\n" + patcher.CLIENT_TEST_NEEDLE + " }\n" + + +def _write_tree( + tmp_path: Path, + util: str, + mod: str, + api_test: str = None, + client_test: str = None, +) -> Path: + server_rust = tmp_path / "aw-server-rust" + endpoints = server_rust / "aw-server" / "src" / "endpoints" endpoints.mkdir(parents=True) (endpoints / "util.rs").write_text(util, encoding="utf-8") (endpoints / "mod.rs").write_text(mod, encoding="utf-8") + (server_rust / "aw-server" / "tests").mkdir() + (server_rust / "aw-server" / "tests" / "api.rs").write_text( + API_TEST_SRC if api_test is None else api_test, encoding="utf-8" + ) + (server_rust / "aw-client-rust" / "tests").mkdir(parents=True) + (server_rust / "aw-client-rust" / "tests" / "test.rs").write_text( + CLIENT_TEST_SRC if client_test is None else client_test, encoding="utf-8" + ) return tmp_path @@ -170,17 +189,46 @@ def test_patch_fails_closed_on_pre_721_endpoints(tmp_path: Path): patcher.patch_tree(root) +def test_upstream_tests_are_rewritten_for_research(tmp_path: Path): + root = _write_tree(tmp_path, UTIL_SRC, MOD_SRC) + patcher.patch_tree(root) + api = (root / "aw-server-rust/aw-server/tests/api.rs").read_text(encoding="utf-8") + client = (root / "aw-server-rust/aw-client-rust/tests/test.rs").read_text( + encoding="utf-8" + ) + assert patcher.CSV_TEST_MARKER in api + assert "Status::Forbidden" in api and "Status::Ok" not in api + assert patcher.CLIENT_TEST_MARKER in client + assert "assert!(!all.buckets.contains_key(&bucketname));" in client + assert "research-participant" in client + assert "import_bucket" not in client + + +@pytest.mark.parametrize("which", ["api_test", "client_test"]) +def test_patch_fails_closed_without_test_anchor(tmp_path: Path, which): + root = _write_tree(tmp_path, UTIL_SRC, MOD_SRC, **{which: "fn changed() {}\n"}) + with pytest.raises(ValueError, match="test insertion point, found 0"): + patcher.patch_tree(root) + # Nothing was written, not even the server edits + assert _util(root) == UTIL_SRC + + def test_live_tree_is_patchable_or_already_patched(): root = Path(__file__).resolve().parents[2] util = root / ENDPOINTS / "util.rs" if not util.is_file(): pytest.skip("aw-server-rust not checked out") - text = util.read_text(encoding="utf-8") - for name, needle, _, marker in patcher.UTIL_EDITS: - assert marker in text or text.count(needle) == 1, ( - f"aw-server-rust no longer matches the research export patch ({name}); " - "update scripts/patch_research_edition_export.py" - ) + checks = [(util, patcher.UTIL_EDITS)] + [ + (root / "aw-server-rust" / relative, edits) + for relative, edits in patcher.TEST_EDITS + ] + for path, edits in checks: + text = path.read_text(encoding="utf-8") + for name, needle, _, marker in edits: + assert marker in text or text.count(needle) == 1, ( + f"aw-server-rust no longer matches the research export patch ({name}); " + "update scripts/patch_research_edition_export.py" + ) def test_sanitizer_allowlist_covers_config_categories():