From 976025a90b0eaf60838d21252da2095d8274f061 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 00:46:38 -0400 Subject: [PATCH 01/12] perf(pro): amortize finalization helper sessions --- crates/ctx-cli/src/pro/client_output.rs | 3 +- .../core_materialization_feed.rs | 23 ++- .../tests/finalization.rs | 173 +++++++++++++++--- crates/ctx-cli/src/semantic/daemon.rs | 4 +- .../ctx-cli/src/semantic/daemon_scheduler.rs | 45 ++++- .../src/semantic/daemon_scheduler_tests.rs | 24 ++- crates/ctx-cli/src/semantic/daemon_wakeup.rs | 4 + .../finalization.rs | 29 +-- 8 files changed, 254 insertions(+), 51 deletions(-) diff --git a/crates/ctx-cli/src/pro/client_output.rs b/crates/ctx-cli/src/pro/client_output.rs index 234c5ca8a..7884a7661 100644 --- a/crates/ctx-cli/src/pro/client_output.rs +++ b/crates/ctx-cli/src/pro/client_output.rs @@ -20,8 +20,9 @@ pub(crate) enum CoreMaterializationSyncOutcome { pub(crate) fn sync_core_materialization( data_root: &Path, index: &ctx_history_index::VerifiedIndex, + should_yield: &mut dyn FnMut() -> bool, ) -> Result { - match core_materialization_feed::sync_generation_pinned_core(data_root, index)? { + match core_materialization_feed::sync_generation_pinned_core(data_root, index, should_yield)? { core_materialization_feed::CoreMaterializationSyncProgress::Finished(report) => { Ok(CoreMaterializationSyncOutcome::Finished { did_work: !report.replayed, diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs index bf294ef16..272e2906c 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs @@ -59,10 +59,11 @@ const CORE_RECORD_PAGE_BUDGET: CoreEventPageBudget = CoreEventPageBudget::new( MAX_CORE_EVENT_DELTA_PAGE_CONTENT_BYTES, ); const PRO_CORE_FINALIZATION_LEASE_OWNER_KIND: &str = "pro_core_finalization"; -// Reuse one authenticated helper across fast quanta, but yield the daemon turn -// after a finite residency. Every exchange retains the independent RPC watchdog. -const CORE_FINALIZATION_BURST_MAX_REQUESTS: usize = 256; -const CORE_FINALIZATION_BURST_MAX_ELAPSED: Duration = Duration::from_secs(30); +// Reuse one authenticated helper across enough bounded quanta to amortize +// recovery and authorization. Live daemon control signals can yield sooner, +// and every exchange retains the independent RPC watchdog. +const CORE_FINALIZATION_BURST_MAX_REQUESTS: usize = 4_096; +const CORE_FINALIZATION_BURST_MAX_ELAPSED: Duration = Duration::from_secs(10 * 60); #[path = "core_materialization_feed/batching.rs"] mod batching; #[path = "core_materialization_feed/ordered_prefetch.rs"] @@ -295,6 +296,7 @@ impl CoreMaterializationConsumer for ProtocolCoreMaterializationConsumer { pub(super) fn sync_generation_pinned_core( data_root: &Path, index: &VerifiedIndex, + should_yield: &mut dyn FnMut() -> bool, ) -> Result { let selection = CoreWorkerLaunchSelection::from_runtime(); let required = BTreeSet::from([Capability::Status, Capability::CoreMaterialization]); @@ -313,7 +315,7 @@ pub(super) fn sync_generation_pinned_core( .validate() .map_err(|error| anyhow!("invalid_response: {}", error.message))?; let mut progress = if status.currentness == CoreProjectionCurrentness::Finalizing { - continue_core_finalization(index, &status, &mut consumer, selection)? + continue_core_finalization(index, &status, &mut consumer, selection, should_yield)? } else { sync_core_feed_progress_with_launch( index, @@ -774,6 +776,7 @@ fn continue_core_finalization( status: &ctx_pro_host_protocol::StatusResult, consumer: &mut C, selection: CoreWorkerLaunchSelection, + should_yield: &mut dyn FnMut() -> bool, ) -> Result { let authenticated_final_deadline_unix = consumer.authenticated_final_deadline_unix(); continue_core_finalization_burst_with( @@ -785,12 +788,13 @@ fn continue_core_finalization( authenticated_final_deadline_unix, Instant::now, crate::pro::commercial_lifecycle::unix_time, + should_yield, || false, ) } #[allow(clippy::too_many_arguments)] -fn continue_core_finalization_burst_with( +fn continue_core_finalization_burst_with( index: &VerifiedIndex, status: &ctx_pro_host_protocol::StatusResult, consumer: &mut C, @@ -799,12 +803,14 @@ fn continue_core_finalization_burst_with( authenticated_final_deadline_unix: Option, mut now: Now, mut unix_now: UnixNow, + mut should_yield: Yield, mut is_cancelled: Cancelled, ) -> Result where C: CoreMaterializationConsumer, Now: FnMut() -> Instant, UnixNow: FnMut() -> Result, + Yield: FnMut() -> bool, Cancelled: FnMut() -> bool, { if limits.max_requests == 0 { @@ -826,6 +832,11 @@ where if is_cancelled() { bail!("helper_cancelled: Core finalization continuation burst cancelled"); } + if should_yield() { + return Ok(CoreMaterializationSyncProgress::FinalizationPending( + pending, + )); + } if requests >= limits.max_requests || now().saturating_duration_since(started) >= limits.max_elapsed { diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs index 909d35410..2884fc2cb 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs @@ -105,6 +105,7 @@ fn continuation_completion_rejects_changed_finish_digest_and_revision() { &status, &mut consumer, CoreWorkerLaunchSelection::explicit_test(1), + &mut || false, ) .unwrap_err(); assert!( @@ -181,6 +182,7 @@ fn finish_yields_once_then_one_restart_session_chains_pending_to_completion() { &status, &mut consumer, CoreWorkerLaunchSelection::explicit_test(1), + &mut || false, ) .unwrap() else { panic!("one continuation session should reach the terminal receipt"); @@ -250,6 +252,7 @@ fn continuation_chains_only_validated_nonstale_ordered_matching_cursors() { &status, &mut consumer, CoreWorkerLaunchSelection::explicit_test(1), + &mut || false, ) .unwrap_err(); assert!( @@ -262,12 +265,12 @@ fn continuation_chains_only_validated_nonstale_ordered_matching_cursors() { #[test] fn production_continuation_burst_is_large_but_still_bounded() { - assert_eq!(CoreFinalizationBurstLimits::PRODUCTION.max_requests, 256); + assert_eq!(CoreFinalizationBurstLimits::PRODUCTION.max_requests, 4_096); assert_eq!( CoreFinalizationBurstLimits::PRODUCTION.max_elapsed, - Duration::from_secs(30) + Duration::from_secs(10 * 60) ); - assert!(CoreFinalizationBurstLimits::PRODUCTION.max_elapsed < BATCH_TIMEOUT); + assert!(BATCH_TIMEOUT < CoreFinalizationBurstLimits::PRODUCTION.max_elapsed); } #[test] @@ -308,6 +311,7 @@ fn continuation_burst_yields_truthfully_at_the_fixed_request_bound() { || started, || Ok(0), || false, + || false, ) .unwrap() else { @@ -363,6 +367,7 @@ fn continuation_burst_yields_truthfully_at_the_elapsed_bound() { || observations.pop_front().unwrap(), || Ok(0), || false, + || false, ) .unwrap() else { @@ -413,6 +418,7 @@ fn continuation_burst_does_not_start_after_its_elapsed_budget() { || observations.pop_front().unwrap(), || Ok(0), || false, + || false, ) .unwrap() else { @@ -458,6 +464,7 @@ fn continuation_burst_stops_before_another_exchange_when_cancelled() { None, || started, || Ok(0), + || false, || { cancellation_checks = cancellation_checks.saturating_add(1); cancellation_checks > 1 @@ -473,6 +480,59 @@ fn continuation_burst_stops_before_another_exchange_when_cancelled() { assert_eq!(consumer.continue_pending.len(), 1); } +#[test] +fn continuation_burst_yields_truthfully_when_daemon_control_is_pending() { + let (_temp, index) = single_source_index( + "finalization-daemon-control-yield.jsonl", + vec!["body".to_owned()], + ); + let initial = progress_for( + &index, + CoreMaterializationFinalizationPhase::EmitEventIndex, + '0', + "test-core-materializer-v1", + ); + let first = advanced_pending(&initial, 1); + let mut consumer = Consumer::new(); + consumer.finalization_progress = Some(initial); + consumer.finish = Some(finish_for(&index, "test-core-materializer-v1")); + consumer.continue_pending.push_back(first.clone()); + consumer + .continue_pending + .push_back(advanced_pending(&first.progress, 2)); + let status = consumer + .status(StatusRequest { + requested_core_generation_id: Some(index.generation_id().to_owned()), + }) + .unwrap(); + let started = Instant::now(); + let mut yield_checks = 0_usize; + let CoreMaterializationSyncProgress::FinalizationPending(observed) = + continue_core_finalization_burst_with( + &index, + &status, + &mut consumer, + CoreWorkerLaunchSelection::explicit_test(1), + CoreFinalizationBurstLimits::PRODUCTION, + None, + || started, + || Ok(0), + || { + yield_checks = yield_checks.saturating_add(1); + yield_checks > 1 + }, + || false, + ) + .unwrap() + else { + panic!("daemon control must preserve truthful Pending status"); + }; + assert_eq!(observed, first); + assert_eq!(consumer.continue_requests.len(), 1); + assert_eq!(consumer.finalization_progress, Some(observed.progress)); + assert_eq!(consumer.continue_pending.len(), 1); +} + #[test] fn continuation_burst_propagates_exchange_errors_without_losing_the_last_cursor() { for message in [ @@ -514,6 +574,7 @@ fn continuation_burst_propagates_exchange_errors_without_losing_the_last_cursor( || started, || Ok(0), || false, + || false, ) .unwrap_err(); assert_eq!(error.to_string(), message); @@ -554,6 +615,7 @@ fn continuation_burst_never_starts_after_the_authenticated_final_deadline() { || started, || Ok(101), || false, + || false, ) .unwrap_err(); assert!(error.to_string().contains("entitlement_expired")); @@ -599,6 +661,7 @@ fn continuation_burst_stops_when_the_final_deadline_elapses_between_exchanges() || started, || Ok(unix_observations.pop_front().unwrap()), || false, + || false, ) .unwrap_err(); assert!(error.to_string().contains("entitlement_expired")); @@ -634,6 +697,7 @@ fn lost_continue_response_restarts_from_the_helpers_durable_cursor() { &status, &mut first_session, CoreWorkerLaunchSelection::explicit_test(1), + &mut || false, ) .unwrap_err(); assert!(error.to_string().contains("response_lost")); @@ -674,6 +738,7 @@ fn lost_continue_response_restarts_from_the_helpers_durable_cursor() { || started, || Ok(0), || false, + || false, ) .unwrap() else { @@ -970,6 +1035,21 @@ fn run_authenticated_process_burst( data_root: &Path, helper_path: &Path, index: &VerifiedIndex, +) -> CoreMaterializationSyncProgress { + run_authenticated_process_burst_with_limits( + data_root, + helper_path, + index, + CoreFinalizationBurstLimits::PRODUCTION, + ) +} + +#[cfg(unix)] +fn run_authenticated_process_burst_with_limits( + data_root: &Path, + helper_path: &Path, + index: &VerifiedIndex, + limits: CoreFinalizationBurstLimits, ) -> CoreMaterializationSyncProgress { let required = BTreeSet::from([Capability::Status, Capability::CoreMaterialization]); let client = ProClient::connect_to_path_with_authorization_mode( @@ -997,11 +1077,18 @@ fn run_authenticated_process_burst( }) .unwrap(); status.validate().unwrap(); - continue_core_finalization( + let authenticated_final_deadline_unix = consumer.authenticated_final_deadline_unix(); + continue_core_finalization_burst_with( index, &status, &mut consumer, CoreWorkerLaunchSelection::explicit_test(1), + limits, + authenticated_final_deadline_unix, + Instant::now, + crate::pro::commercial_lifecycle::unix_time, + || false, + || false, ) .unwrap() } @@ -1101,13 +1188,8 @@ fn one_authenticated_client_lifetime_services_a_bounded_cursor_burst() { #[test] #[ignore = "focused authenticated helper lifetime/exchange-count microbenchmark"] fn authenticated_helper_lifetime_count_microbenchmark() { - const PRIOR_BURST_MAX_REQUESTS: usize = 16; - const BEFORE_HELPER_LIFETIMES: usize = 7_461; - const BEFORE_WALL: &str = "53:38.58"; - const BEFORE_USER_CPU_SECONDS: u64 = 2_690; - const BEFORE_SYSTEM_CPU_SECONDS: u64 = 744; - const SEPARATE_SEGMENT_CAP_FAILURE: usize = 4_097; - const MEASURED_EXCHANGES: usize = 65; + const PRIOR_BURST_MAX_REQUESTS: usize = 256; + const MEASURED_EXCHANGES: usize = 1_025; let terminal_after = MEASURED_EXCHANGES; let temp = tempdir().unwrap(); let data_root = temp.path().join("data"); @@ -1128,7 +1210,7 @@ fn authenticated_helper_lifetime_count_microbenchmark() { serde_json::to_vec(&serde_json::json!({ "kind": "pending", "continue_count": 0, - "progress": initial, + "progress": initial.clone(), })) .unwrap(), ) @@ -1141,6 +1223,44 @@ fn authenticated_helper_lifetime_count_microbenchmark() { &receipt, ); + let prior_limits = CoreFinalizationBurstLimits { + max_requests: PRIOR_BURST_MAX_REQUESTS, + max_elapsed: Duration::from_secs(30), + }; + let prior_started = Instant::now(); + let mut prior_measured_lifetimes = 0_usize; + loop { + prior_measured_lifetimes = prior_measured_lifetimes.saturating_add(1); + if matches!( + run_authenticated_process_burst_with_limits( + &data_root, + &helper_path, + &index, + prior_limits, + ), + CoreMaterializationSyncProgress::Finished(_) + ) { + break; + } + } + let prior_elapsed = prior_started.elapsed(); + let prior_measured_hellos = process_log_count(&log_path, "hello"); + let prior_measured_authorizations = process_log_count(&log_path, "authorize"); + let prior_measured_exchanges = process_log_count(&log_path, "continue:"); + + fs::write( + &state_path, + serde_json::to_vec(&serde_json::json!({ + "kind": "pending", + "continue_count": 0, + "progress": initial, + })) + .unwrap(), + ) + .unwrap(); + fs::write(&log_path, []).unwrap(); + + let measured_started = Instant::now(); let mut measured_lifetimes = 0_usize; loop { measured_lifetimes = measured_lifetimes.saturating_add(1); @@ -1151,6 +1271,7 @@ fn authenticated_helper_lifetime_count_microbenchmark() { break; } } + let measured_elapsed = measured_started.elapsed(); let measured_hellos = process_log_count(&log_path, "hello"); let measured_authorizations = process_log_count(&log_path, "authorize"); let measured_exchanges = process_log_count(&log_path, "continue:"); @@ -1161,29 +1282,29 @@ fn authenticated_helper_lifetime_count_microbenchmark() { measured_lifetimes, terminal_after.div_ceil(CORE_FINALIZATION_BURST_MAX_REQUESTS) ); - let prior_measured_lifetimes = terminal_after.div_ceil(PRIOR_BURST_MAX_REQUESTS); + assert_eq!(prior_measured_exchanges, terminal_after); + assert_eq!(prior_measured_hellos, prior_measured_lifetimes); + assert_eq!(prior_measured_authorizations, prior_measured_lifetimes); + assert_eq!( + prior_measured_lifetimes, + terminal_after.div_ceil(PRIOR_BURST_MAX_REQUESTS) + ); assert_eq!(prior_measured_lifetimes, 5); assert_eq!(measured_lifetimes, 1); + assert!( + measured_elapsed < prior_elapsed, + "amortized helper wall {measured_elapsed:?} did not beat prior wall {prior_elapsed:?}" + ); let measured_reduction_percent = 100.0 * (1.0 - measured_lifetimes as f64 / prior_measured_lifetimes as f64); assert!(measured_reduction_percent >= 80.0); - let projected_prior_burst_lifetimes = - BEFORE_HELPER_LIFETIMES.div_ceil(PRIOR_BURST_MAX_REQUESTS); - let projected_burst_lifetimes = - BEFORE_HELPER_LIFETIMES.div_ceil(CORE_FINALIZATION_BURST_MAX_REQUESTS); - let projected_reduction_from_prior_percent = - 100.0 * (1.0 - projected_burst_lifetimes as f64 / projected_prior_burst_lifetimes as f64); - let projected_reduction_from_original_percent = - 100.0 * (1.0 - projected_burst_lifetimes as f64 / BEFORE_HELPER_LIFETIMES as f64); eprintln!( - "authenticated_finalization_burst before_lifetimes={BEFORE_HELPER_LIFETIMES} before_wall={BEFORE_WALL} before_user_cpu_seconds={BEFORE_USER_CPU_SECONDS} before_system_cpu_seconds={BEFORE_SYSTEM_CPU_SECONDS} separate_segment_cap_failure={SEPARATE_SEGMENT_CAP_FAILURE} prior_burst_max_requests={PRIOR_BURST_MAX_REQUESTS} production_burst_max_requests={CORE_FINALIZATION_BURST_MAX_REQUESTS} production_burst_max_elapsed_seconds={} measured_exchanges={measured_exchanges} prior_measured_connect_hello_lifetimes={prior_measured_lifetimes} measured_connect_hello_lifetimes={measured_lifetimes} measured_reduction_from_prior_percent={measured_reduction_percent:.2} projected_prior_burst_lifetimes={projected_prior_burst_lifetimes} projected_burst_lifetimes={projected_burst_lifetimes} projected_reduction_from_prior_percent={projected_reduction_from_prior_percent:.2} projected_reduction_from_original_percent={projected_reduction_from_original_percent:.2}", + "authenticated_finalization_burst prior_burst_max_requests={PRIOR_BURST_MAX_REQUESTS} production_burst_max_requests={CORE_FINALIZATION_BURST_MAX_REQUESTS} production_burst_max_elapsed_seconds={} measured_exchanges={measured_exchanges} prior_measured_connect_hello_lifetimes={prior_measured_lifetimes} prior_measured_wall_seconds={:.6} measured_connect_hello_lifetimes={measured_lifetimes} measured_wall_seconds={:.6} measured_lifetime_reduction_percent={measured_reduction_percent:.2}", CORE_FINALIZATION_BURST_MAX_ELAPSED.as_secs(), + prior_elapsed.as_secs_f64(), + measured_elapsed.as_secs_f64(), ); - assert_eq!(projected_prior_burst_lifetimes, 467); - assert_eq!(projected_burst_lifetimes, 30); - assert!(projected_reduction_from_prior_percent > 90.0); - assert!(projected_reduction_from_original_percent > 99.0); } #[test] diff --git a/crates/ctx-cli/src/semantic/daemon.rs b/crates/ctx-cli/src/semantic/daemon.rs index c68e75731..f2585a8ea 100644 --- a/crates/ctx-cli/src/semantic/daemon.rs +++ b/crates/ctx-cli/src/semantic/daemon.rs @@ -134,6 +134,7 @@ pub(super) struct DaemonRuntime { pub(super) consumer_retry_deferral: DaemonConsumerRetryDeferral, pub(super) background_refresh_cadence: DaemonBackgroundRefreshCadence, pub(super) config: AppConfig, + pub(super) wakeup: Option>, } #[cfg(test)] @@ -320,6 +321,7 @@ pub(super) fn run_daemon_inner( let mut failed = false; let mut runtime = DaemonRuntime { config: config.clone(), + wakeup: Some(Arc::clone(&wakeup)), ..DaemonRuntime::default() }; let mut config_reload = DaemonConfigReloadState::pending(config); @@ -594,7 +596,7 @@ pub(super) fn run_daemon_inner( &config_reload.to_json(), )?; failed |= iteration.failed; - if continue_immediately { + if continue_immediately && !wakeup.has_pending() { continue; } observe_daemon_query_activity( diff --git a/crates/ctx-cli/src/semantic/daemon_scheduler.rs b/crates/ctx-cli/src/semantic/daemon_scheduler.rs index 433f738f8..757106318 100644 --- a/crates/ctx-cli/src/semantic/daemon_scheduler.rs +++ b/crates/ctx-cli/src/semantic/daemon_scheduler.rs @@ -589,7 +589,10 @@ fn run_pro_catch_up_with_retry( continuation_pending: false, }); } - let run = run_after_core_publication(data_root, core_generation_id, authority)?; + let mut yield_control = ProFinalizationYieldControl::new(runtime.wakeup.clone()); + let run = run_after_core_publication(data_root, core_generation_id, authority, &mut || { + yield_control.requested(data_root) + })?; let status = record_daemon_job_retry(&mut runtime.pro_retry, run.status); persist_pro_status(data_root, &status)?; Ok(SourceBackedProCatchUpRun { @@ -599,6 +602,40 @@ fn run_pro_catch_up_with_retry( }) } +const PRO_FINALIZATION_CONFIG_POLL_INTERVAL: StdDuration = StdDuration::from_secs(1); + +struct ProFinalizationYieldControl { + wakeup: Option>, + next_config_poll: Instant, +} + +impl ProFinalizationYieldControl { + fn new(wakeup: Option>) -> Self { + Self { + wakeup, + next_config_poll: Instant::now(), + } + } + + fn requested(&mut self, data_root: &Path) -> bool { + if self + .wakeup + .as_deref() + .is_some_and(DaemonWakeup::has_pending) + { + return true; + } + let now = Instant::now(); + if now < self.next_config_poll { + return false; + } + self.next_config_poll = now + PRO_FINALIZATION_CONFIG_POLL_INTERVAL; + AppConfig::load(data_root).is_ok_and(|config| { + !config.daemon.enabled || config.daemon.mode.runs_only_source_refresh() + }) + } +} + fn prepare_pro_retry_for_generation( runtime: &mut DaemonRuntime, data_root: &Path, @@ -998,6 +1035,7 @@ pub(super) fn daemon_deadline_has_min_budget(deadline: Option, min_secs } use std::{ path::Path, + sync::Arc, time::{Duration as StdDuration, Instant}, }; @@ -1013,12 +1051,15 @@ use crate::{ DaemonBacklogV1, DaemonBackoffV1, DaemonCoverageV1, DaemonCycleStateV1, DaemonHistoryFreshnessV1, }, - compact_json, DaemonRunArgs, DaemonStartModeArg, + compact_json, + config::AppConfig, + DaemonRunArgs, DaemonStartModeArg, }; use super::{ daemon::{DaemonIteration, DaemonRuntime}, daemon_retry::{semantic_failure_class_from_job, DaemonRetryBackoff, SemanticFailureClass}, + daemon_wakeup::DaemonWakeup, daemon_worker::{ daemon_semantic_failed_job, daemon_semantic_retry_backoff_job, run_daemon_semantic_job, }, diff --git a/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs b/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs index 661975bb4..26a5bd9c3 100644 --- a/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs +++ b/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs @@ -3,6 +3,7 @@ mod background_refresh; use std::{ collections::{BTreeMap, BTreeSet}, + fs, path::Path, sync::{ atomic::{AtomicUsize, Ordering}, @@ -29,6 +30,7 @@ use crate::{ output::JsonOutputFormat, semantic::{ daemon::{daemon_wait_duration, install_daemon_test_job_hooks, DaemonTestJobHooks}, + daemon_wakeup::DaemonWakeup, source_backed_refresh_coordinator::EventWatermark, source_backed_refresh_coordinator::{ coordinate_source_backed_refresh, publish_authoritative_empty_generation_for_test, @@ -55,12 +57,30 @@ use super::{ run_daemon_scheduler_cycle_with_activity, run_pending_core_pro_catch_up, run_pending_core_pro_catch_up_with, run_pending_core_refresh, run_pro_catch_up_with_retry, write_daemon_job_status, DaemonBackgroundRefreshCadence, DaemonRetryBackoff, DaemonRuntime, - SourceBackedProCatchUpRun, SourceBackedProCoreAuthority, DAEMON_BACKGROUND_REFRESH_MAX_REST, - DAEMON_BACKGROUND_REFRESH_MIN_REST, + ProFinalizationYieldControl, SourceBackedProCatchUpRun, SourceBackedProCoreAuthority, + DAEMON_BACKGROUND_REFRESH_MAX_REST, DAEMON_BACKGROUND_REFRESH_MIN_REST, }; const READINESS_QUERY: &str = "readiness-boundary-regression"; +#[test] +fn pro_finalization_yield_control_observes_shutdown_and_config_disable() { + let temp = tempfile::tempdir().unwrap(); + let wakeup = Arc::new(DaemonWakeup::default()); + let mut shutdown_control = ProFinalizationYieldControl::new(Some(Arc::clone(&wakeup))); + + wakeup.signal_shutdown(); + assert!(shutdown_control.requested(temp.path())); + + fs::write( + temp.path().join("config.toml"), + "[daemon]\nenabled = false\n", + ) + .unwrap(); + let mut config_control = ProFinalizationYieldControl::new(None); + assert!(config_control.requested(temp.path())); +} + fn daemon_args() -> DaemonRunArgs { DaemonRunArgs { foreground: false, diff --git a/crates/ctx-cli/src/semantic/daemon_wakeup.rs b/crates/ctx-cli/src/semantic/daemon_wakeup.rs index 87066cd8a..c9c995235 100644 --- a/crates/ctx-cli/src/semantic/daemon_wakeup.rs +++ b/crates/ctx-cli/src/semantic/daemon_wakeup.rs @@ -276,6 +276,10 @@ impl DaemonWakeup { self.signal(WAKE_SHUTDOWN); } + pub(super) fn has_pending(&self) -> bool { + self.lock_state().pending != 0 + } + fn signal(&self, reason: u8) { let mut state = self.lock_state(); state.pending |= reason; diff --git a/crates/ctx-cli/src/semantic/source_backed_pro_catch_up/finalization.rs b/crates/ctx-cli/src/semantic/source_backed_pro_catch_up/finalization.rs index 0040adc2d..cc2146d9b 100644 --- a/crates/ctx-cli/src/semantic/source_backed_pro_catch_up/finalization.rs +++ b/crates/ctx-cli/src/semantic/source_backed_pro_catch_up/finalization.rs @@ -34,6 +34,7 @@ pub(in crate::semantic) fn run_after_core_publication( data_root: &Path, core_generation_id: &str, authority: SourceBackedProCoreAuthority<'_>, + should_yield: &mut dyn FnMut() -> bool, ) -> Result { if authority.generation_id() != core_generation_id { return record_preflight_error( @@ -55,20 +56,22 @@ pub(in crate::semantic) fn run_after_core_publication( }, preflight_core_materialization, |data_root, index| { - Ok(match sync_core_materialization(data_root, index)? { - CoreMaterializationSyncOutcome::Finished { - receipt, - did_work, - helper_artifact_sha256, - } => ProCatchUpSyncOutcome::Finished { - receipt, - did_work, - helper_artifact_sha256, + Ok( + match sync_core_materialization(data_root, index, should_yield)? { + CoreMaterializationSyncOutcome::Finished { + receipt, + did_work, + helper_artifact_sha256, + } => ProCatchUpSyncOutcome::Finished { + receipt, + did_work, + helper_artifact_sha256, + }, + CoreMaterializationSyncOutcome::FinalizationPending { pending } => { + ProCatchUpSyncOutcome::FinalizationPending { pending } + } }, - CoreMaterializationSyncOutcome::FinalizationPending { pending } => { - ProCatchUpSyncOutcome::FinalizationPending { pending } - } - }) + ) }, ) } From 4a325aa7241e2bfc7150c1b8d5e488c7fecfb48d Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:29:06 -0400 Subject: [PATCH 02/12] fix(pro): preserve forced finalization progress --- crates/ctx-cli/src/semantic/daemon.rs | 2 + .../ctx-cli/src/semantic/daemon_scheduler.rs | 8 +- .../src/semantic/daemon_scheduler_tests.rs | 118 +++++++++++++++++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/crates/ctx-cli/src/semantic/daemon.rs b/crates/ctx-cli/src/semantic/daemon.rs index f2585a8ea..93fb0faa4 100644 --- a/crates/ctx-cli/src/semantic/daemon.rs +++ b/crates/ctx-cli/src/semantic/daemon.rs @@ -135,6 +135,7 @@ pub(super) struct DaemonRuntime { pub(super) background_refresh_cadence: DaemonBackgroundRefreshCadence, pub(super) config: AppConfig, pub(super) wakeup: Option>, + pub(super) force: bool, } #[cfg(test)] @@ -322,6 +323,7 @@ pub(super) fn run_daemon_inner( let mut runtime = DaemonRuntime { config: config.clone(), wakeup: Some(Arc::clone(&wakeup)), + force: args.force, ..DaemonRuntime::default() }; let mut config_reload = DaemonConfigReloadState::pending(config); diff --git a/crates/ctx-cli/src/semantic/daemon_scheduler.rs b/crates/ctx-cli/src/semantic/daemon_scheduler.rs index 757106318..d4180ab15 100644 --- a/crates/ctx-cli/src/semantic/daemon_scheduler.rs +++ b/crates/ctx-cli/src/semantic/daemon_scheduler.rs @@ -589,7 +589,7 @@ fn run_pro_catch_up_with_retry( continuation_pending: false, }); } - let mut yield_control = ProFinalizationYieldControl::new(runtime.wakeup.clone()); + let mut yield_control = ProFinalizationYieldControl::new(runtime.wakeup.clone(), runtime.force); let run = run_after_core_publication(data_root, core_generation_id, authority, &mut || { yield_control.requested(data_root) })?; @@ -606,13 +606,15 @@ const PRO_FINALIZATION_CONFIG_POLL_INTERVAL: StdDuration = StdDuration::from_sec struct ProFinalizationYieldControl { wakeup: Option>, + force: bool, next_config_poll: Instant, } impl ProFinalizationYieldControl { - fn new(wakeup: Option>) -> Self { + fn new(wakeup: Option>, force: bool) -> Self { Self { wakeup, + force, next_config_poll: Instant::now(), } } @@ -631,7 +633,7 @@ impl ProFinalizationYieldControl { } self.next_config_poll = now + PRO_FINALIZATION_CONFIG_POLL_INTERVAL; AppConfig::load(data_root).is_ok_and(|config| { - !config.daemon.enabled || config.daemon.mode.runs_only_source_refresh() + (!self.force && !config.daemon.enabled) || config.daemon.mode.runs_only_source_refresh() }) } } diff --git a/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs b/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs index 26a5bd9c3..489537458 100644 --- a/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs +++ b/crates/ctx-cli/src/semantic/daemon_scheduler_tests.rs @@ -67,7 +67,7 @@ const READINESS_QUERY: &str = "readiness-boundary-regression"; fn pro_finalization_yield_control_observes_shutdown_and_config_disable() { let temp = tempfile::tempdir().unwrap(); let wakeup = Arc::new(DaemonWakeup::default()); - let mut shutdown_control = ProFinalizationYieldControl::new(Some(Arc::clone(&wakeup))); + let mut shutdown_control = ProFinalizationYieldControl::new(Some(Arc::clone(&wakeup)), false); wakeup.signal_shutdown(); assert!(shutdown_control.requested(temp.path())); @@ -77,8 +77,122 @@ fn pro_finalization_yield_control_observes_shutdown_and_config_disable() { "[daemon]\nenabled = false\n", ) .unwrap(); - let mut config_control = ProFinalizationYieldControl::new(None); + let mut config_control = ProFinalizationYieldControl::new(None, false); assert!(config_control.requested(temp.path())); + + let mut forced_control = ProFinalizationYieldControl::new(None, true); + assert!(!forced_control.requested(temp.path())); + + fs::write( + temp.path().join("config.toml"), + "[daemon]\nenabled = false\nmode = \"source-refresh-only\"\n", + ) + .unwrap(); + let mut forced_source_only_control = ProFinalizationYieldControl::new(None, true); + assert!(forced_source_only_control.requested(temp.path())); +} + +#[test] +fn forced_disabled_daemon_advances_pending_finalization_and_can_exit_finitely() { + let temp = tempfile::tempdir().unwrap(); + fs::write( + temp.path().join("config.toml"), + "[daemon]\nenabled = false\n", + ) + .unwrap(); + let generation = publish_empty_core_generation(temp.path()); + persist_pro_status( + temp.path(), + &json!({ + "schema_version": 1, + "owner": "daemon", + "kind": "source_backed_pro_catch_up", + "status": "pending", + "pending": true, + "retryable": false, + "core_generation_id": generation, + "receipt_core_generation_id": null, + "finalization_progress": { + "materialization_id": "a".repeat(64), + "core_generation_id": generation, + "finish_request_digest": "c".repeat(64), + "materializer_revision": "materializer-v1", + "phase": "validate_candidate", + "cursor_sha256": "b".repeat(64), + }, + "attempts": 1, + "last_attempt_at_ms": 1, + "last_attempt_duration_us": 1, + "error_code": null, + "last_error": null, + "reason": "finalizing", + "consecutive_failures": 0, + "retry_after_ms": null, + "retry_not_before_at_ms": null, + }), + ) + .unwrap(); + let mut runtime = DaemonRuntime { + config: AppConfig::load(temp.path()).unwrap(), + force: true, + ..DaemonRuntime::default() + }; + runtime.sidecar_drain.pro_attempted_generation = Some(generation.clone()); + let mut progress_calls = 0_u32; + + let progressed = run_pending_core_pro_catch_up_with( + temp.path(), + &mut runtime, + None, + |data_root, runtime, selected, authority| { + assert_eq!(selected, generation); + assert_eq!(authority.generation_id(), generation); + let mut yield_control = + ProFinalizationYieldControl::new(runtime.wakeup.clone(), runtime.force); + assert!(!yield_control.requested(data_root)); + progress_calls += 1; + let status = json!({ + "schema_version": 1, + "owner": "daemon", + "kind": "source_backed_pro_catch_up", + "status": "completed", + "pending": false, + "retryable": false, + "core_generation_id": selected, + "receipt_core_generation_id": selected, + "attempts": 2, + "last_attempt_at_ms": 2, + "last_attempt_duration_us": 1, + "error_code": null, + "last_error": null, + }); + persist_pro_status(data_root, &status)?; + Ok(SourceBackedProCatchUpRun { + status, + did_work: true, + continuation_pending: false, + }) + }, + ) + .unwrap() + .expect("forced disabled daemon should run pending finalization"); + + assert!(progressed.did_work); + assert!(progressed.continue_immediately); + assert_eq!(progress_calls, 1); + assert_eq!(read_pro_status(temp.path()).unwrap()["status"], "completed"); + let drained = run_pending_core_pro_catch_up_with( + temp.path(), + &mut runtime, + None, + |_data_root, _runtime, _generation, _authority| { + panic!("completed finalization must not launch again") + }, + ) + .unwrap(); + assert!(drained.is_none()); + // With no continuation or relaunch left, the finite daemon can enter its + // existing idle-exit path instead of livelocking on pending finalization. } fn daemon_args() -> DaemonRunArgs { From 7cee639c53a18d2394dc0d431262744d6c2c0dae Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:52:20 -0400 Subject: [PATCH 03/12] fix(protocol): accept Pro checkpoint v7 evidence --- .../examples/generate_protocol_inventory/messages.rs | 2 +- crates/ctx-pro-host-protocol/src/message.rs | 10 +++++----- crates/ctx-pro-host-protocol/src/tests.rs | 8 ++++---- .../ctx-pro-host-protocol/testdata/v3/inventory.json | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs index 68ef0756a..566bdb89f 100644 --- a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs +++ b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs @@ -179,7 +179,7 @@ fn helper_messages(fingerprint: &str) -> Vec<(&'static str, HelperMessage)> { storage_evidence: Some(ProStorageEvidence { graph_manifest_schema: 3, flat_format_version: 2, - materializer_checkpoint_version: 6, + materializer_checkpoint_version: 7, journal_pack_format_version: 3, legacy_journals_written: 0, journal_pages_written: 2, diff --git a/crates/ctx-pro-host-protocol/src/message.rs b/crates/ctx-pro-host-protocol/src/message.rs index 0d524abdb..5f3b816c4 100644 --- a/crates/ctx-pro-host-protocol/src/message.rs +++ b/crates/ctx-pro-host-protocol/src/message.rs @@ -473,13 +473,13 @@ pub struct ProStorageEvidence { impl ProStorageEvidence { pub fn validate(&self) -> Result<(), ProtocolError> { - // V4 and V5 remain admissible only as authenticated status evidence - // while a private helper control-loads a prior active graph for - // migration. New publication evidence is V6; this DTO grants no - // EventIndex read compatibility between those storage generations. + // V4 through V6 remain admissible only as authenticated status + // evidence while a helper control-loads a prior active graph for + // migration. Accepting a version here grants no EventIndex read + // compatibility between those storage generations. if self.graph_manifest_schema != 3 || self.flat_format_version != 2 - || !matches!(self.materializer_checkpoint_version, 4 | 5 | 6) + || !matches!(self.materializer_checkpoint_version, 4 | 5 | 6 | 7) || self.journal_pack_format_version != 3 || self.legacy_journals_written != 0 { diff --git a/crates/ctx-pro-host-protocol/src/tests.rs b/crates/ctx-pro-host-protocol/src/tests.rs index 2ce5395b8..588e27406 100644 --- a/crates/ctx-pro-host-protocol/src/tests.rs +++ b/crates/ctx-pro-host-protocol/src/tests.rs @@ -549,11 +549,11 @@ fn storage_evidence_is_strict_and_required_nullable() { } #[test] -fn storage_evidence_accepts_legacy_control_v4_v5_and_current_v6() { +fn storage_evidence_accepts_v4_through_v7_only() { let evidence = ProStorageEvidence { graph_manifest_schema: 3, flat_format_version: 2, - materializer_checkpoint_version: 6, + materializer_checkpoint_version: 7, journal_pack_format_version: 3, legacy_journals_written: 0, journal_pages_written: 2, @@ -565,12 +565,12 @@ fn storage_evidence_accepts_legacy_control_v4_v5_and_current_v6() { }, }; - for checkpoint_version in [4, 5, 6] { + for checkpoint_version in [4, 5, 6, 7] { let mut accepted = evidence.clone(); accepted.materializer_checkpoint_version = checkpoint_version; accepted.validate().unwrap(); } - for checkpoint_version in [0, 3, 7, u16::MAX] { + for checkpoint_version in [0, 3, 8, u16::MAX] { let mut rejected = evidence.clone(); rejected.materializer_checkpoint_version = checkpoint_version; assert_eq!(rejected.validate().unwrap_err().class, ErrorClass::Sequence); diff --git a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json index b9cc53a30..947a87886 100644 --- a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json +++ b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json @@ -1151,7 +1151,7 @@ "graph_key_deleted": "43545850524f0003000000817b2273657175656e6365223a332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c65746564222c22626f6479223a7b2264656c65746564223a747275657d7d7d", "graph_key_deletion_prepared": "43545850524f0003000000e97b2273657175656e6365223a322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c6574696f6e5f7072657061726564222c22626f6479223a7b226368616c6c656e67655f62617365363475726c223a2242675947426759474267594742675947426759474267594742675947426759474267594742675947426759222c22657870697265735f61745f756e6978223a3230302c226b65795f70726573656e74223a747275657d7d7d", "hello": "43545850524f0003000001bd7b2273657175656e6365223a302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2268656c6c6f222c22626f6479223a7b2270726f746f636f6c5f76657273696f6e223a332c2270726f746f636f6c5f66696e6765727072696e74223a2230616233633434356262356237643433336665353635316131643364396131306566373734643537306231333266353065656231343566333033343163376134222c2268656c7065725f76657273696f6e223a22676f6c64656e2d68656c706572222c226361706162696c6974696573223a5b22656e7469746c656d656e745f617574686f72697a6174696f6e222c2267726170685f6b65795f64656c6574696f6e222c22737461747573222c22636f72655f6d6174657269616c697a6174696f6e222c227175657279222c226769745f72656164225d2c22617574686f72697a6174696f6e5f6368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151227d7d7d", - "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a332c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a362c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" + "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a332c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a372c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" }, "host_frames": { "apply_core_event_delta_page": "43545850524f0003000011867b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d7d7d7d", From ecdd0db192c4b9dc1913d1ca026016a06cb942b7 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 04:28:45 -0400 Subject: [PATCH 04/12] fix(protocol): accept Pro checkpoint v8 evidence --- .../generate_protocol_inventory/messages.rs | 2 +- crates/ctx-pro-host-protocol/src/message.rs | 8 ++++---- crates/ctx-pro-host-protocol/src/tests.rs | 14 +++++++++++--- .../testdata/v3/inventory.json | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs index 566bdb89f..3a83ae749 100644 --- a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs +++ b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs @@ -179,7 +179,7 @@ fn helper_messages(fingerprint: &str) -> Vec<(&'static str, HelperMessage)> { storage_evidence: Some(ProStorageEvidence { graph_manifest_schema: 3, flat_format_version: 2, - materializer_checkpoint_version: 7, + materializer_checkpoint_version: 8, journal_pack_format_version: 3, legacy_journals_written: 0, journal_pages_written: 2, diff --git a/crates/ctx-pro-host-protocol/src/message.rs b/crates/ctx-pro-host-protocol/src/message.rs index 5f3b816c4..91d07645a 100644 --- a/crates/ctx-pro-host-protocol/src/message.rs +++ b/crates/ctx-pro-host-protocol/src/message.rs @@ -473,13 +473,13 @@ pub struct ProStorageEvidence { impl ProStorageEvidence { pub fn validate(&self) -> Result<(), ProtocolError> { - // V4 through V6 remain admissible only as authenticated status + // V4 through V7 remain admissible only as authenticated status // evidence while a helper control-loads a prior active graph for - // migration. Accepting a version here grants no EventIndex read - // compatibility between those storage generations. + // migration. V8 is current publication evidence; accepting a version + // here grants no EventIndex read compatibility between generations. if self.graph_manifest_schema != 3 || self.flat_format_version != 2 - || !matches!(self.materializer_checkpoint_version, 4 | 5 | 6 | 7) + || !matches!(self.materializer_checkpoint_version, 4 | 5 | 6 | 7 | 8) || self.journal_pack_format_version != 3 || self.legacy_journals_written != 0 { diff --git a/crates/ctx-pro-host-protocol/src/tests.rs b/crates/ctx-pro-host-protocol/src/tests.rs index 588e27406..5b2019a5a 100644 --- a/crates/ctx-pro-host-protocol/src/tests.rs +++ b/crates/ctx-pro-host-protocol/src/tests.rs @@ -549,11 +549,11 @@ fn storage_evidence_is_strict_and_required_nullable() { } #[test] -fn storage_evidence_accepts_v4_through_v7_only() { +fn storage_evidence_accepts_v4_through_v8_only() { let evidence = ProStorageEvidence { graph_manifest_schema: 3, flat_format_version: 2, - materializer_checkpoint_version: 7, + materializer_checkpoint_version: 8, journal_pack_format_version: 3, legacy_journals_written: 0, journal_pages_written: 2, @@ -564,13 +564,21 @@ fn storage_evidence_accepts_v4_through_v7_only() { started_after_preparation: true, }, }; + evidence.validate().unwrap(); + + let mut invalid_v8 = evidence.clone(); + invalid_v8.legacy_journals_written = 1; + assert_eq!( + invalid_v8.validate().unwrap_err().class, + ErrorClass::Sequence + ); for checkpoint_version in [4, 5, 6, 7] { let mut accepted = evidence.clone(); accepted.materializer_checkpoint_version = checkpoint_version; accepted.validate().unwrap(); } - for checkpoint_version in [0, 3, 8, u16::MAX] { + for checkpoint_version in [0, 3, 9, u16::MAX] { let mut rejected = evidence.clone(); rejected.materializer_checkpoint_version = checkpoint_version; assert_eq!(rejected.validate().unwrap_err().class, ErrorClass::Sequence); diff --git a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json index 947a87886..76d2885e4 100644 --- a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json +++ b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json @@ -1151,7 +1151,7 @@ "graph_key_deleted": "43545850524f0003000000817b2273657175656e6365223a332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c65746564222c22626f6479223a7b2264656c65746564223a747275657d7d7d", "graph_key_deletion_prepared": "43545850524f0003000000e97b2273657175656e6365223a322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c6574696f6e5f7072657061726564222c22626f6479223a7b226368616c6c656e67655f62617365363475726c223a2242675947426759474267594742675947426759474267594742675947426759474267594742675947426759222c22657870697265735f61745f756e6978223a3230302c226b65795f70726573656e74223a747275657d7d7d", "hello": "43545850524f0003000001bd7b2273657175656e6365223a302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2268656c6c6f222c22626f6479223a7b2270726f746f636f6c5f76657273696f6e223a332c2270726f746f636f6c5f66696e6765727072696e74223a2230616233633434356262356237643433336665353635316131643364396131306566373734643537306231333266353065656231343566333033343163376134222c2268656c7065725f76657273696f6e223a22676f6c64656e2d68656c706572222c226361706162696c6974696573223a5b22656e7469746c656d656e745f617574686f72697a6174696f6e222c2267726170685f6b65795f64656c6574696f6e222c22737461747573222c22636f72655f6d6174657269616c697a6174696f6e222c227175657279222c226769745f72656164225d2c22617574686f72697a6174696f6e5f6368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151227d7d7d", - "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a332c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a372c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" + "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a332c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a382c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" }, "host_frames": { "apply_core_event_delta_page": "43545850524f0003000011867b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d7d7d7d", From 49460617cc56fc2a50192d9093b69af8af1bb64e Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:01:59 -0400 Subject: [PATCH 05/12] test(cli): align raw output authority inventory --- crates/ctx-cli/tests/raw_output_policy.rs | 4 +-- .../tests/raw_output_policy/allowlist.rs | 32 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/crates/ctx-cli/tests/raw_output_policy.rs b/crates/ctx-cli/tests/raw_output_policy.rs index 46dc789d0..0391f3c31 100644 --- a/crates/ctx-cli/tests/raw_output_policy.rs +++ b/crates/ctx-cli/tests/raw_output_policy.rs @@ -1408,10 +1408,10 @@ fn production_raw_output_inventory_is_closed() { assert_eq!( build_script_counts, BTreeMap::from([ - ("build.rs", 6), + ("build.rs", 7), ("crates/ctx-semantic-model/build.rs", 2), ]), - "Cargo build-script output authority must remain exactly six CLI directives and two model directives" + "Cargo build-script output authority must remain exactly seven CLI directives and two model directives" ); let diff = compare_policy(sites, ALLOWLIST); assert!(diff.is_closed(), "{}", diff.render()); diff --git a/crates/ctx-cli/tests/raw_output_policy/allowlist.rs b/crates/ctx-cli/tests/raw_output_policy/allowlist.rs index 1ad3c5812..998342e89 100644 --- a/crates/ctx-cli/tests/raw_output_policy/allowlist.rs +++ b/crates/ctx-cli/tests/raw_output_policy/allowlist.rs @@ -236,7 +236,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( CLI_BUILD, - "main#2@885cbe6e55e728c7", + "main#2@148f1a006bdde4df", PrintMacro, MachineProtocol, CARGO_DIRECTIVE, @@ -244,7 +244,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( CLI_BUILD, - "main#3@cfdf571c6929ef24", + "main#3@885cbe6e55e728c7", PrintMacro, MachineProtocol, CARGO_DIRECTIVE, @@ -252,7 +252,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( CLI_BUILD, - "main#4@56760ef03ecfb333", + "main#4@cfdf571c6929ef24", PrintMacro, MachineProtocol, CARGO_DIRECTIVE, @@ -260,7 +260,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( CLI_BUILD, - "main#5@0acadf523050c898", + "main#5@56760ef03ecfb333", PrintMacro, MachineProtocol, CARGO_DIRECTIVE, @@ -268,7 +268,15 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( CLI_BUILD, - "main#6@17e10d71b62c3756", + "main#6@0acadf523050c898", + PrintMacro, + MachineProtocol, + CARGO_DIRECTIVE, + GATE + ), + allow!( + CLI_BUILD, + "main#7@17e10d71b62c3756", PrintMacro, MachineProtocol, CARGO_DIRECTIVE, @@ -484,7 +492,15 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( DISPATCH, - "run_cli#1@64c8afd2f04c16a3", + "run_cli#1@2912ee43f3606c5f", + PrintMacro, + MachineProtocol, + JSON_PROTOCOL, + UNIT + ), + allow!( + DISPATCH, + "run_cli#2@64c8afd2f04c16a3", PrintMacro, MachineProtocol, JSON_PROTOCOL, @@ -492,7 +508,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( DISPATCH, - "run_cli#2@638b02f9a8248d06", + "run_cli#3@638b02f9a8248d06", PrintMacro, MachineProtocol, JSON_PROTOCOL, @@ -500,7 +516,7 @@ pub(super) const ALLOWLIST: &[AllowEntry] = &[ ), allow!( DISPATCH, - "run_cli#3@1f99826fdc74e99b", + "run_cli#4@1f99826fdc74e99b", PrintMacro, MachineProtocol, "generic machine-mode command error", From bdcabffb9b958d0c0371d0ffbbd772a45b057de0 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:15:18 -0400 Subject: [PATCH 06/12] perf(pro): carry exact event feed lengths --- .../core_materialization_feed/batching.rs | 245 +++++++++++++++--- .../tests/materialization.rs | 142 ++++++++-- 2 files changed, 329 insertions(+), 58 deletions(-) diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/batching.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/batching.rs index b145b9fe6..1fed1c627 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/batching.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/batching.rs @@ -2,6 +2,20 @@ use super::*; pub(super) const MAX_CORE_EVENT_DELTA_BATCH_EXCHANGES: usize = MAX_CORE_EVENT_DELTA_PAGES * 2 - 1; +const EMPTY_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES: usize = b"{\"pages\":[]}".len(); +const HOST_ENVELOPE_SEQUENCE_PREFIX_BYTES: usize = b"{\"sequence\":".len(); +const HOST_ENVELOPE_REQUEST_ID_PREFIX_BYTES: usize = b",\"request_id\":".len(); +const HOST_ENVELOPE_UUID_WIRE_BYTES: usize = 38; +const HOST_ENVELOPE_EVENT_DELTA_PAGES_PREFIX_BYTES: usize = + b",\"message\":{\"kind\":\"apply_core_event_delta_pages\",\"body\":".len(); +const HOST_ENVELOPE_SUFFIX_BYTES: usize = b"}}".len(); +const ADDED_EVENT_DELTA_PREFIX_BYTES: usize = b"{\"kind\":\"added\",\"value\":".len(); +const ADDED_EVENT_DELTA_SUFFIX_BYTES: usize = b"}".len(); +const REPLACED_EVENT_DELTA_PREFIX_BYTES: usize = + b"{\"kind\":\"replaced\",\"value\":{\"prior_core_record_sha256\":".len(); +const REPLACED_EVENT_DELTA_RECORD_PREFIX_BYTES: usize = b",\"record\":".len(); +const REPLACED_EVENT_DELTA_SUFFIX_BYTES: usize = b"}}".len(); + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum EventDeltaExchangeMode { Normal, @@ -167,6 +181,7 @@ pub(super) struct PreparedEventDelta { delta: CoreEventDelta, record_json: Option, content_bytes: usize, + wire_bytes: Option, } impl PreparedEventDelta { @@ -176,6 +191,7 @@ impl PreparedEventDelta { delta: CoreEventDelta::Added(record.record), record_json: Some(record.stored_json), content_bytes, + wire_bytes: None, } } @@ -191,6 +207,7 @@ impl PreparedEventDelta { }), record_json: Some(record.stored_json), content_bytes, + wire_bytes: None, } } @@ -199,6 +216,7 @@ impl PreparedEventDelta { delta: CoreEventDelta::Tombstoned(tombstone), record_json: None, content_bytes: 0, + wire_bytes: None, } } @@ -216,11 +234,14 @@ impl PreparedEventDelta { content_bytes, } }); - Ok(Self { + let mut prepared = Self { delta, record_json, content_bytes, - }) + wire_bytes: None, + }; + prepared.ensure_wire_bytes()?; + Ok(prepared) } #[cfg(test)] @@ -232,6 +253,21 @@ impl PreparedEventDelta { pub(super) const fn content_bytes(&self) -> usize { self.content_bytes } + + fn ensure_wire_bytes(&mut self) -> Result { + if let Some(wire_bytes) = self.wire_bytes { + return Ok(wire_bytes); + } + let wire_bytes = prepared_event_delta_encoded_len(self)?; + self.wire_bytes = Some(wire_bytes); + Ok(wire_bytes) + } + + fn carried_wire_bytes(&self) -> Result { + self.wire_bytes.ok_or_else(|| { + anyhow!("internal: Core event delta reached page assembly without a carried length") + }) + } } pub(super) struct EventDeltaPageBuilder { @@ -265,13 +301,15 @@ impl EventDeltaPageBuilder { pub(super) fn try_push( &mut self, - delta: PreparedEventDelta, + mut delta: PreparedEventDelta, ) -> Result> { if self.deltas.len() == MAX_CORE_EVENT_DELTA_PAGE_ITEMS { return Ok(Some(delta)); } - let Some((content_bytes, wire_bytes)) = self.prospective_bytes(&delta)? else { + let delta_wire_bytes = delta.ensure_wire_bytes()?; + let Some((content_bytes, wire_bytes)) = self.prospective_bytes(&delta, delta_wire_bytes) + else { return Ok(Some(delta)); }; if content_bytes > MAX_CORE_EVENT_DELTA_PAGE_CONTENT_BYTES @@ -286,12 +324,13 @@ impl EventDeltaPageBuilder { Ok(None) } - pub(super) fn push_split_overflow(&mut self, delta: PreparedEventDelta) -> Result<()> { + pub(super) fn push_split_overflow(&mut self, mut delta: PreparedEventDelta) -> Result<()> { if !self.deltas.is_empty() { bail!("internal: Core event overflow page was not empty"); } + let delta_wire_bytes = delta.ensure_wire_bytes()?; let (content_bytes, wire_bytes) = self - .prospective_bytes(&delta)? + .prospective_bytes(&delta, delta_wire_bytes) .ok_or_else(|| anyhow!("invalid_request: Core event delta page bytes overflowed"))?; self.deltas.push(delta); self.content_bytes = content_bytes; @@ -299,12 +338,15 @@ impl EventDeltaPageBuilder { Ok(()) } - fn prospective_bytes(&self, delta: &PreparedEventDelta) -> Result> { + fn prospective_bytes( + &self, + delta: &PreparedEventDelta, + delta_wire_bytes: usize, + ) -> Option<(usize, usize)> { let delta_content_bytes = delta.content_bytes; let Some(content_bytes) = self.content_bytes.checked_add(delta_content_bytes) else { - return Ok(None); + return None; }; - let delta_wire_bytes = prepared_event_delta_encoded_len(delta)?; // The empty envelope already includes `[]`. A first delta replaces the // empty vector contents; every later delta adds one comma as well. let separator_bytes = usize::from(!self.deltas.is_empty()); @@ -313,9 +355,9 @@ impl EventDeltaPageBuilder { .checked_add(separator_bytes) .and_then(|bytes| bytes.checked_add(delta_wire_bytes)) else { - return Ok(None); + return None; }; - Ok(Some((content_bytes, wire_bytes))) + Some((content_bytes, wire_bytes)) } pub(super) fn is_empty(&self) -> bool { @@ -351,6 +393,7 @@ pub(super) struct PreparedEventDeltaPage { pub(super) struct PreparedEventDeltaPagesRequest { request: ApplyCoreEventDeltaPagesRequest, record_json: Vec>>, + page_wire_bytes: Vec, encoded_request_bytes: usize, } @@ -394,6 +437,7 @@ impl PreparedEventDeltaPagesRequest { } pub(super) fn write_request_json(&self, writer: &mut impl Write) -> Result<()> { + self.validate_alignment()?; write_all(writer, b"{\"pages\":[")?; for (index, (page, record_json)) in self.request.pages.iter().zip(&self.record_json).enumerate() @@ -430,16 +474,16 @@ impl PreparedEventDeltaPagesRequest { sequence: u64, request_id: uuid::Uuid, ) -> Result<()> { - let mut encoded = EncodedLength::default(); - self.write_host_envelope_json(&mut encoded, sequence, request_id)?; - if encoded.bytes > ctx_pro_host_protocol::MAX_FRAME_PAYLOAD_BYTES { + let payload_bytes = + prepared_event_delta_pages_frame_payload_bytes(sequence, self.encoded_request_bytes)?; + if payload_bytes > ctx_pro_host_protocol::MAX_FRAME_PAYLOAD_BYTES { bail!( "invalid_request: prepared Core event frame has {} payload bytes; maximum is {}", - encoded.bytes, + payload_bytes, ctx_pro_host_protocol::MAX_FRAME_PAYLOAD_BYTES ); } - let payload_len = u32::try_from(encoded.bytes) + let payload_len = u32::try_from(payload_bytes) .map_err(|_| anyhow!("invalid_request: Core event frame length overflowed"))?; write_all(writer, ctx_pro_host_protocol::FRAME_MAGIC)?; write_all( @@ -464,10 +508,12 @@ impl PreparedEventDeltaPagesRequest { fn split_off(&mut self, at: usize) -> Result { let pages = self.request.pages.split_off(at); let record_json = self.record_json.split_off(at); + let page_wire_bytes = self.page_wire_bytes.split_off(at); self.remeasure()?; let mut right = Self { request: ApplyCoreEventDeltaPagesRequest { pages }, record_json, + page_wire_bytes, encoded_request_bytes: 0, }; right.remeasure()?; @@ -475,9 +521,21 @@ impl PreparedEventDeltaPagesRequest { } fn remeasure(&mut self) -> Result<()> { - let mut encoded = EncodedLength::default(); - self.write_request_json(&mut encoded)?; - self.encoded_request_bytes = encoded.bytes; + self.validate_alignment()?; + self.encoded_request_bytes = checked_json_items_len( + EMPTY_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES, + self.page_wire_bytes.iter().copied().map(Ok), + "Core event delta page batch length overflowed", + )?; + Ok(()) + } + + fn validate_alignment(&self) -> Result<()> { + if self.request.pages.len() != self.record_json.len() + || self.request.pages.len() != self.page_wire_bytes.len() + { + bail!("internal: prepared Core event page bytes were misaligned"); + } Ok(()) } } @@ -490,8 +548,7 @@ pub(super) struct EventDeltaPageBatchBuilder { impl EventDeltaPageBatchBuilder { pub(super) fn new() -> Result { - let empty_wire_bytes = - encoded_json_len(&ApplyCoreEventDeltaPagesRequest { pages: Vec::new() })?; + let empty_wire_bytes = EMPTY_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES; Ok(Self { pages: Vec::new(), empty_wire_bytes, @@ -543,18 +600,21 @@ impl EventDeltaPageBatchBuilder { } pub(super) fn take_request(&mut self) -> Result { + let encoded_request_bytes = self.wire_bytes; self.wire_bytes = self.empty_wire_bytes; let pages = std::mem::take(&mut self.pages); - let (pages, record_json): (Vec<_>, Vec<_>) = pages + let (pages_and_records, page_wire_bytes): (Vec<_>, Vec<_>) = pages .into_iter() - .map(|page| (page.page, page.record_json)) + .map(|page| ((page.page, page.record_json), page.wire_bytes)) .unzip(); - let mut request = PreparedEventDeltaPagesRequest { + let (pages, record_json): (Vec<_>, Vec<_>) = pages_and_records.into_iter().unzip(); + let request = PreparedEventDeltaPagesRequest { request: ApplyCoreEventDeltaPagesRequest { pages }, record_json, - encoded_request_bytes: 0, + page_wire_bytes, + encoded_request_bytes, }; - request.remeasure()?; + request.validate_alignment()?; Ok(request) } @@ -563,6 +623,61 @@ impl EventDeltaPageBatchBuilder { } } +fn checked_encoded_sum( + parts: impl IntoIterator, + overflow_message: &'static str, +) -> Result { + parts.into_iter().try_fold(0_usize, |total, part| { + total + .checked_add(part) + .ok_or_else(|| anyhow!("invalid_request: {overflow_message}")) + }) +} + +fn checked_json_items_len( + empty_wire_bytes: usize, + item_wire_bytes: impl IntoIterator>, + overflow_message: &'static str, +) -> Result { + item_wire_bytes.into_iter().enumerate().try_fold( + empty_wire_bytes, + |total, (index, item_wire_bytes)| { + let item_wire_bytes = item_wire_bytes?; + total + .checked_add(usize::from(index != 0)) + .and_then(|total| total.checked_add(item_wire_bytes)) + .ok_or_else(|| anyhow!("invalid_request: {overflow_message}")) + }, + ) +} + +const fn u64_decimal_bytes(mut value: u64) -> usize { + let mut bytes = 1; + while value >= 10 { + value /= 10; + bytes += 1; + } + bytes +} + +pub(super) fn prepared_event_delta_pages_frame_payload_bytes( + sequence: u64, + encoded_request_bytes: usize, +) -> Result { + checked_encoded_sum( + [ + HOST_ENVELOPE_SEQUENCE_PREFIX_BYTES, + u64_decimal_bytes(sequence), + HOST_ENVELOPE_REQUEST_ID_PREFIX_BYTES, + HOST_ENVELOPE_UUID_WIRE_BYTES, + HOST_ENVELOPE_EVENT_DELTA_PAGES_PREFIX_BYTES, + encoded_request_bytes, + HOST_ENVELOPE_SUFFIX_BYTES, + ], + "Core event frame length overflowed", + ) +} + #[derive(Default)] struct EncodedLength { bytes: usize, @@ -601,9 +716,28 @@ fn write_json_value(writer: &mut impl Write, value: &impl serde::Serialize) -> R } fn prepared_event_delta_encoded_len(delta: &PreparedEventDelta) -> Result { - let mut encoded = EncodedLength::default(); - write_prepared_event_delta_json(&mut encoded, &delta.delta, delta.record_json.as_ref())?; - Ok(encoded.bytes) + match (&delta.delta, delta.record_json.as_ref()) { + (CoreEventDelta::Added(_), Some(record_json)) => checked_encoded_sum( + [ + ADDED_EVENT_DELTA_PREFIX_BYTES, + record_json.bytes()?.len(), + ADDED_EVENT_DELTA_SUFFIX_BYTES, + ], + "Core event delta length overflowed", + ), + (CoreEventDelta::Replaced(replacement), Some(record_json)) => checked_encoded_sum( + [ + REPLACED_EVENT_DELTA_PREFIX_BYTES, + encoded_json_len(&replacement.prior_core_record_sha256)?, + REPLACED_EVENT_DELTA_RECORD_PREFIX_BYTES, + record_json.bytes()?.len(), + REPLACED_EVENT_DELTA_SUFFIX_BYTES, + ], + "Core event delta length overflowed", + ), + (CoreEventDelta::Tombstoned(_), None) => encoded_json_len(&delta.delta), + _ => bail!("internal: prepared Core event delta record bytes were misaligned"), + } } fn write_prepared_event_delta_json( @@ -614,6 +748,8 @@ fn write_prepared_event_delta_json( match (delta, record_json) { (CoreEventDelta::Added(_), Some(record_json)) => { write_all(writer, b"{\"kind\":\"added\",\"value\":")?; + #[cfg(test)] + note_prepared_record_body_write(); write_all(writer, record_json.bytes()?)?; write_all(writer, b"}") } @@ -624,6 +760,8 @@ fn write_prepared_event_delta_json( )?; write_json_value(writer, &replacement.prior_core_record_sha256)?; write_all(writer, b",\"record\":")?; + #[cfg(test)] + note_prepared_record_body_write(); write_all(writer, record_json.bytes()?)?; write_all(writer, b"}}") } @@ -632,6 +770,26 @@ fn write_prepared_event_delta_json( } } +#[cfg(test)] +std::thread_local! { + static PREPARED_RECORD_BODY_WRITES: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(test)] +fn note_prepared_record_body_write() { + PREPARED_RECORD_BODY_WRITES.set(PREPARED_RECORD_BODY_WRITES.get().saturating_add(1)); +} + +#[cfg(test)] +pub(super) fn reset_prepared_record_body_writes() { + PREPARED_RECORD_BODY_WRITES.set(0); +} + +#[cfg(test)] +pub(super) fn prepared_record_body_writes() -> usize { + PREPARED_RECORD_BODY_WRITES.get() +} + fn write_prepared_event_delta_page_json( writer: &mut impl Write, page: &CoreEventDeltaPage, @@ -726,6 +884,25 @@ pub(super) fn prepared_event_delta_page( deltas: Vec, expected_wire_bytes: usize, ) -> Result { + let empty_page = unvalidated_event_delta_page( + materialization_id, + generation_id, + reconciliation, + page_index, + terminal, + Vec::new(), + ); + let carried_wire_bytes = checked_json_items_len( + encoded_json_len(&empty_page)?, + deltas.iter().map(PreparedEventDelta::carried_wire_bytes), + "Core event delta page length overflowed", + )?; + if carried_wire_bytes != expected_wire_bytes { + bail!("internal: carried Core event delta page length was not exact"); + } + if carried_wire_bytes > MAX_CORE_EVENT_DELTA_PAGE_WIRE_BYTES { + bail!("invalid_request: Core event delta page exceeds its wire bound"); + } let (deltas, record_json): (Vec<_>, Vec<_>) = deltas .into_iter() .map(|delta| (delta.delta, delta.record_json)) @@ -738,18 +915,10 @@ pub(super) fn prepared_event_delta_page( terminal, deltas, ); - let mut encoded = EncodedLength::default(); - write_prepared_event_delta_page_json(&mut encoded, &page, &record_json)?; - if encoded.bytes != expected_wire_bytes { - bail!("internal: carried Core event delta page length was not exact"); - } - if encoded.bytes > MAX_CORE_EVENT_DELTA_PAGE_WIRE_BYTES { - bail!("invalid_request: Core event delta page exceeds its wire bound"); - } Ok(PreparedEventDeltaPage { page, record_json, - wire_bytes: encoded.bytes, + wire_bytes: carried_wire_bytes, }) } diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs index 316898d6c..251e4b094 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs @@ -240,6 +240,7 @@ fn prepared_request_reuses_stored_core_json_and_matches_legacy_frames_byte_for_b }; expected_request.validate().unwrap(); + reset_prepared_record_body_writes(); let mut builder = EventDeltaPageBatchBuilder::new().unwrap(); let mut page_builder = EventDeltaPageBuilder::new( TEST_MATERIALIZATION_ID, @@ -267,9 +268,11 @@ fn prepared_request_reuses_stored_core_json_and_matches_legacy_frames_byte_for_b ) .unwrap(); let prepared = builder.take_request().unwrap(); + assert_eq!(prepared_record_body_writes(), 0); let canonical_request = serde_json::to_vec(&expected_request).unwrap(); let mut manual_request = Vec::new(); prepared.write_request_json(&mut manual_request).unwrap(); + assert_eq!(prepared_record_body_writes(), 2); assert_eq!(manual_request, canonical_request); assert_eq!(prepared.encoded_request_bytes(), canonical_request.len()); assert_eq!( @@ -290,27 +293,126 @@ fn prepared_request_reuses_stored_core_json_and_matches_legacy_frames_byte_for_b } ); - let sequence = 17; let request_id = uuid::Uuid::from_u128(0x1234); - let expected_envelope = ctx_pro_host_protocol::HostEnvelope { - sequence, - request_id, - message: HostMessage::ApplyCoreEventDeltaPages(expected_request.clone()), - }; - let mut canonical_frame = Vec::new(); - ctx_pro_host_protocol::write_frame(&mut canonical_frame, &expected_envelope).unwrap(); - let mut prepared_frame = Vec::new(); - prepared - .write_frame(&mut prepared_frame, sequence, request_id) - .unwrap(); - assert_eq!(prepared_frame, canonical_frame); - let decoded: ctx_pro_host_protocol::HostEnvelope = - ctx_pro_host_protocol::read_frame(&mut prepared_frame.as_slice()).unwrap(); - assert_eq!(decoded, expected_envelope); - let HostMessage::ApplyCoreEventDeltaPages(request) = &decoded.message else { - panic!("prepared frame decoded as the wrong request kind") - }; - request.validate().unwrap(); + for sequence in [0, 9, 10, 99, 100, 999, 1_000, u64::MAX] { + let expected_envelope = ctx_pro_host_protocol::HostEnvelope { + sequence, + request_id, + message: HostMessage::ApplyCoreEventDeltaPages(expected_request.clone()), + }; + let mut canonical_frame = Vec::new(); + ctx_pro_host_protocol::write_frame(&mut canonical_frame, &expected_envelope).unwrap(); + reset_prepared_record_body_writes(); + let mut prepared_frame = Vec::new(); + prepared + .write_frame(&mut prepared_frame, sequence, request_id) + .unwrap(); + assert_eq!(prepared_record_body_writes(), 2); + assert_eq!(prepared_frame, canonical_frame); + assert_eq!( + prepared_event_delta_pages_frame_payload_bytes( + sequence, + prepared.encoded_request_bytes() + ) + .unwrap(), + canonical_frame.len() - ctx_pro_host_protocol::FRAME_HEADER_BYTES + ); + let decoded: ctx_pro_host_protocol::HostEnvelope = + ctx_pro_host_protocol::read_frame(&mut prepared_frame.as_slice()).unwrap(); + assert_eq!(decoded, expected_envelope); + let HostMessage::ApplyCoreEventDeltaPages(request) = &decoded.message else { + panic!("prepared frame decoded as the wrong request kind") + }; + request.validate().unwrap(); + } +} + +#[test] +fn prepared_multi_page_split_retry_uses_carried_lengths_and_single_pass_frames() { + let source = source("prepared-split-retry.jsonl"); + let pages = single_delta_event_pages(&source, 3, 17); + reset_prepared_record_body_writes(); + let mut builder = EventDeltaPageBatchBuilder::new().unwrap(); + for page in pages { + assert!(builder + .try_push(prepared_event_delta_page_from_typed(page).unwrap()) + .unwrap() + .is_none()); + } + let request = builder.take_request().unwrap(); + assert_eq!(prepared_record_body_writes(), 0); + + let mut attempts = Vec::new(); + let request_id = uuid::Uuid::from_u128(0x5678); + reset_prepared_record_body_writes(); + apply_prepared_batched_event_delta_pages_with(request, &mut |request, _remaining| { + let sequence = u64::try_from(attempts.len()).unwrap(); + let mut frame = Vec::new(); + request + .write_frame(&mut frame, sequence, request_id) + .unwrap(); + let envelope: ctx_pro_host_protocol::HostEnvelope = + ctx_pro_host_protocol::read_frame(&mut frame.as_slice()).unwrap(); + let HostMessage::ApplyCoreEventDeltaPages(typed) = &envelope.message else { + panic!("prepared frame decoded as the wrong request kind") + }; + attempts.push( + typed + .pages + .iter() + .map(|page| page.page_index) + .collect::>(), + ); + let mut canonical = Vec::new(); + ctx_pro_host_protocol::write_frame(&mut canonical, &envelope).unwrap(); + assert_eq!(frame, canonical); + if typed.pages.len() > 1 { + Ok(HelperMessage::Error( + ctx_pro_host_protocol::ProtocolError::new( + ErrorClass::Bounds, + "synthetic pre-mutation bound", + ), + )) + } else { + Ok(successful_plural_response(&envelope.message)) + } + }) + .unwrap(); + + assert_eq!( + attempts, + vec![vec![0, 1, 2], vec![0], vec![1, 2], vec![1], vec![2]] + ); + assert_eq!(prepared_record_body_writes(), 8); +} + +#[test] +fn carried_length_overflow_paths_remain_typed_and_bounded() { + let frame_error = + prepared_event_delta_pages_frame_payload_bytes(u64::MAX, usize::MAX).unwrap_err(); + assert_eq!( + frame_error.to_string(), + "invalid_request: Core event frame length overflowed" + ); + + let source = source("carried-length-overflow.jsonl"); + let reconciliation = reconciliation(&source); + let mut builder = EventDeltaPageBuilder::new( + TEST_MATERIALIZATION_ID, + TEST_GENERATION_ID, + &reconciliation, + 0, + ) + .unwrap(); + builder.wire_bytes = usize::MAX; + let delta = PreparedEventDelta::from_typed(CoreEventDelta::Added(record( + &source, + 1, + "body".to_owned(), + ))) + .unwrap(); + assert!(builder.try_push(delta).unwrap().is_some()); + assert!(builder.is_empty()); } #[test] From bf039fd2dcaf140aea263057b1b4edc21bdcbaa3 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:18:04 -0400 Subject: [PATCH 07/12] perf(protocol): reuse validated Core page lengths --- .../src/core_materialization.rs | 19 +- .../core_materialization/event_delta_pages.rs | 82 ++++---- .../src/core_materialization/tests.rs | 175 +++++++++++++++--- 3 files changed, 208 insertions(+), 68 deletions(-) diff --git a/crates/ctx-pro-host-protocol/src/core_materialization.rs b/crates/ctx-pro-host-protocol/src/core_materialization.rs index d4b660084..dee5b474a 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization.rs @@ -688,6 +688,12 @@ pub struct CoreEventDeltaPage { impl CoreEventDeltaPage { pub fn validate(&self) -> Result<(), ProtocolError> { + self.validate_and_encoded_len().map(|_| ()) + } + + /// Runs the complete public page validation and returns the exact compact + /// JSON length measured by that validation pass. + pub(super) fn validate_and_encoded_len(&self) -> Result { validate_core_event_delta_page_header( &self.materialization_id, &self.core_generation_id, @@ -738,11 +744,14 @@ impl CoreEventDeltaPage { "Core event delta page exceeds its selected-content byte bound", )); } - validate_encoded_bound( - self, - MAX_CORE_EVENT_DELTA_PAGE_WIRE_BYTES, - "Core event delta page exceeds its wire bound", - ) + let encoded_bytes = encoded_len(self)?; + if encoded_bytes > MAX_CORE_EVENT_DELTA_PAGE_WIRE_BYTES { + return Err(ProtocolError::new( + ErrorClass::Bounds, + "Core event delta page exceeds its wire bound", + )); + } + Ok(encoded_bytes) } pub fn content_bytes(&self) -> Result { diff --git a/crates/ctx-pro-host-protocol/src/core_materialization/event_delta_pages.rs b/crates/ctx-pro-host-protocol/src/core_materialization/event_delta_pages.rs index 54dc89812..b83f6a160 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization/event_delta_pages.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization/event_delta_pages.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; use super::{ - core_source_delta_exact_eq, encoded_len, invalid_contract, validate_encoded_bound, - validate_sha256, CoreEventDelta, CoreEventDeltaPage, CoreEventDeltaPageApplied, ErrorClass, - ProtocolError, SourceKey, MAX_CORE_CONTROL_WIRE_BYTES, + core_source_delta_exact_eq, invalid_contract, validate_encoded_bound, validate_sha256, + CoreEventDelta, CoreEventDeltaPage, CoreEventDeltaPageApplied, ErrorClass, ProtocolError, + SourceKey, MAX_CORE_CONTROL_WIRE_BYTES, }; pub const MAX_CORE_EVENT_DELTA_PAGES: usize = 16; @@ -21,12 +21,7 @@ pub struct ApplyCoreEventDeltaPagesRequest { impl ApplyCoreEventDeltaPagesRequest { pub fn validate(&self) -> Result<(), ProtocolError> { - validate_event_delta_page_envelope(self.pages.iter(), CoreEventDeltaPage::validate)?; - validate_encoded_bound( - self, - MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES, - "Core event delta page batch exceeds its aggregate wire bound", - ) + self.validate_and_encoded_len().map(|_| ()) } /// Validates an ordered envelope of prepared pages using the complete @@ -35,29 +30,7 @@ impl ApplyCoreEventDeltaPagesRequest { pages: impl ExactSizeIterator, ) -> Result<(), ProtocolError> { let pages = pages.collect::>(); - validate_event_delta_page_envelope(pages.iter().copied(), CoreEventDeltaPage::validate)?; - let encoded_request_bytes = pages.iter().enumerate().try_fold( - b"{\"pages\":[]}".len(), - |total, (index, page)| -> Result { - let page_bytes = encoded_len(*page)?; - total - .checked_add(usize::from(index != 0)) - .and_then(|total| total.checked_add(page_bytes)) - .ok_or_else(|| { - ProtocolError::new( - ErrorClass::Bounds, - "Core event delta page batch length overflowed", - ) - }) - }, - )?; - if encoded_request_bytes > MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES { - return Err(ProtocolError::new( - ErrorClass::Bounds, - "Core event delta page batch exceeds its aggregate wire bound", - )); - } - Ok(()) + validate_event_delta_page_envelope(pages.iter().copied()).map(|_| ()) } /// Captures only the fields needed to validate the ordered batch @@ -90,8 +63,8 @@ impl ApplyCoreEventDeltaPagesRequest { &self, encoded_request_bytes: usize, ) -> Result { - self.validate()?; - if encoded_request_bytes != encoded_len(self)? { + let validated_encoded_request_bytes = self.validate_and_encoded_len()?; + if encoded_request_bytes != validated_encoded_request_bytes { return Err(ProtocolError::new( ErrorClass::InvalidRequest, "prepared Core event delta page batch length does not match canonical JSON", @@ -115,12 +88,15 @@ impl ApplyCoreEventDeltaPagesRequest { pages, }) } + + fn validate_and_encoded_len(&self) -> Result { + validate_event_delta_page_envelope(self.pages.iter()) + } } fn validate_event_delta_page_envelope<'a>( mut pages: impl ExactSizeIterator, - validate_page: fn(&CoreEventDeltaPage) -> Result<(), ProtocolError>, -) -> Result<(), ProtocolError> { +) -> Result { if pages.len() == 0 || pages.len() > MAX_CORE_EVENT_DELTA_PAGES { return Err(ProtocolError::new( ErrorClass::Bounds, @@ -131,7 +107,8 @@ fn validate_event_delta_page_envelope<'a>( let first = pages .next() .expect("non-empty Core event envelope was checked above"); - validate_page(first)?; + let mut encoded_page_bytes = Vec::with_capacity(pages.len() + 1); + encoded_page_bytes.push(first.validate_and_encoded_len()?); let mut prior_page_index = first.page_index; let mut prior_event_id = first.deltas.last().map(|delta| delta.event_id().digest()); let mut prior_terminal = first.terminal; @@ -140,7 +117,7 @@ fn validate_event_delta_page_envelope<'a>( let mut prior_reconciliation = &first.reconciliation.delta; let mut seen_sources = vec![prior_source]; for page in pages { - validate_page(page)?; + encoded_page_bytes.push(page.validate_and_encoded_len()?); if page.materialization_id != first.materialization_id || page.core_generation_id != first.core_generation_id { @@ -183,7 +160,34 @@ fn validate_event_delta_page_envelope<'a>( prior_reconciliation = &page.reconciliation.delta; } - Ok(()) + let encoded_request_bytes = + checked_core_event_delta_pages_request_len(encoded_page_bytes.into_iter())?; + if encoded_request_bytes > MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES { + return Err(ProtocolError::new( + ErrorClass::Bounds, + "Core event delta page batch exceeds its aggregate wire bound", + )); + } + Ok(encoded_request_bytes) +} + +pub(super) fn checked_core_event_delta_pages_request_len( + encoded_page_bytes: impl Iterator, +) -> Result { + encoded_page_bytes.enumerate().try_fold( + b"{\"pages\":[]}".len(), + |total, (index, page_bytes)| { + total + .checked_add(usize::from(index != 0)) + .and_then(|total| total.checked_add(page_bytes)) + .ok_or_else(|| { + ProtocolError::new( + ErrorClass::Bounds, + "Core event delta page batch length overflowed", + ) + }) + }, + ) } fn batch_sequence_error() -> ProtocolError { diff --git a/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs b/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs index 6a45981f2..7beac6c80 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs @@ -568,6 +568,35 @@ fn event_delta_pages_request( ApplyCoreEventDeltaPagesRequest { pages } } +fn event_delta_pages_request_with_large_nested_bodies() -> ApplyCoreEventDeltaPagesRequest { + let mut request = event_delta_pages_request(3, true); + for (index, page) in request.pages.iter_mut().enumerate() { + let CoreEventDelta::Added(record) = &mut page.deltas[0] else { + panic!("expected added Core event"); + }; + record.content.normalized_body = Some(format!( + "{}normalized-tail-{index}", + "large body line with unicode 🦀\n".repeat(16 * 1024) + )); + let escaped = format!( + "{}structured-tail-{index}", + "nested\0line\nquote\"slash\\".repeat(16 * 1024) + ); + record.content.structured_content = Some(serde_json::json!({ + "outer": { + "messages": [{ + "parts": [ + {"kind": "text", "body": escaped}, + {"kind": "metadata", "values": [index, index + 1, index + 2]}, + ], + }], + }, + })); + record.validate_contract().unwrap(); + } + request +} + fn multi_source_event_delta_pages_request( pages_per_source: &[usize], ) -> ApplyCoreEventDeltaPagesRequest { @@ -955,6 +984,12 @@ fn event_delta_page_batch_enforces_exact_aggregate_encoded_request_bound() { let mut request = request_with_encoded_wire_bytes(MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES); request.validate().unwrap(); + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(request.pages.iter()).unwrap(); + request + .acknowledgement_identity_for_prepared_request( + MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES, + ) + .unwrap(); let CoreEventDelta::Added(record) = &mut request.pages[0].deltas[0] else { panic!("expected added Core event"); @@ -965,32 +1000,111 @@ fn event_delta_page_batch_enforces_exact_aggregate_encoded_request_bound() { MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES + 1 ); request.pages[0].validate().unwrap(); - assert_eq!(request.validate().unwrap_err().class, ErrorClass::Bounds); + let expected = request.validate().unwrap_err(); + assert_eq!(expected.class, ErrorClass::Bounds); + assert_eq!( + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(request.pages.iter()) + .unwrap_err(), + expected + ); + assert_eq!( + request + .acknowledgement_identity_for_prepared_request( + MAX_CORE_EVENT_DELTA_PAGES_REQUEST_WIRE_BYTES + 1, + ) + .unwrap_err(), + expected + ); } #[test] -fn prepared_event_delta_page_batch_checks_complete_pages_and_exact_length() { - let request = event_delta_pages_request(2, true); +fn prepared_event_delta_page_batch_reuses_exact_large_nested_lengths_and_identity() { + let request = event_delta_pages_request_with_large_nested_bodies(); let encoded_request_bytes = serde_json::to_vec(&request).unwrap().len(); + assert!(encoded_request_bytes > 1024 * 1024); + + let encoded_page_bytes = request + .pages + .iter() + .map(|page| { + let measured = page.validate_and_encoded_len().unwrap(); + assert_eq!(measured, serde_json::to_vec(page).unwrap().len()); + measured + }) + .collect::>(); + assert_eq!( + event_delta_pages::checked_core_event_delta_pages_request_len( + encoded_page_bytes.into_iter() + ) + .unwrap(), + encoded_request_bytes + ); + + request.validate().unwrap(); ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(request.pages.iter()).unwrap(); - request + let ordinary_identity = request.acknowledgement_identity().unwrap(); + let prepared_identity = request .acknowledgement_identity_for_prepared_request(encoded_request_bytes) .unwrap(); - assert_eq!( - request - .acknowledgement_identity_for_prepared_request(encoded_request_bytes - 1) - .unwrap_err() - .class, - ErrorClass::InvalidRequest + assert_eq!(prepared_identity, ordinary_identity); + + let mismatch = ProtocolError::new( + ErrorClass::InvalidRequest, + "prepared Core event delta page batch length does not match canonical JSON", ); + for mismatched_bytes in [ + encoded_request_bytes - 1, + encoded_request_bytes + 1, + usize::MAX, + ] { + assert_eq!( + request + .acknowledgement_identity_for_prepared_request(mismatched_bytes) + .unwrap_err(), + mismatch + ); + } +} - let mut invalid = request; - invalid.pages[0].materialization_id = "not-a-digest".to_owned(); +#[test] +fn prepared_and_typed_event_delta_page_batch_rejections_are_identical() { + let assert_rejection_parity = |request: &ApplyCoreEventDeltaPagesRequest| { + let expected = request.validate().unwrap_err(); + assert_eq!( + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(request.pages.iter()) + .unwrap_err(), + expected + ); + assert_eq!( + request + .acknowledgement_identity_for_prepared_request( + serde_json::to_vec(request).unwrap().len(), + ) + .unwrap_err(), + expected + ); + }; + + assert_rejection_parity(&ApplyCoreEventDeltaPagesRequest { pages: Vec::new() }); + + let mut malformed_page = event_delta_pages_request(2, true); + malformed_page.pages[0].materialization_id = "not-a-digest".to_owned(); + assert_rejection_parity(&malformed_page); + + let mut invalid_sequence = event_delta_pages_request(2, true); + invalid_sequence.pages[1].page_index += 1; + assert_rejection_parity(&invalid_sequence); +} + +#[test] +fn prepared_event_delta_page_batch_length_accounting_rejects_overflow() { assert_eq!( - ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(invalid.pages.iter()) - .unwrap_err() - .class, - ErrorClass::InvalidRequest + event_delta_pages::checked_core_event_delta_pages_request_len([usize::MAX].into_iter()) + .unwrap_err(), + ProtocolError::new( + ErrorClass::Bounds, + "Core event delta page batch length overflowed" + ) ); } @@ -1049,29 +1163,42 @@ fn event_delta_page_batch_retains_every_constituent_page_bound() { }; let too_many_items = request(vec!["x".to_owned(); MAX_CORE_EVENT_DELTA_PAGE_ITEMS + 1]); + let too_many_items_error = too_many_items.validate().unwrap_err(); + assert_eq!(too_many_items_error.class, ErrorClass::Bounds); assert_eq!( - too_many_items.validate().unwrap_err().class, - ErrorClass::Bounds + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(too_many_items.pages.iter()) + .unwrap_err(), + too_many_items_error ); let too_much_content = request(vec![ "x".repeat(MAX_CORE_EVENT_DELTA_PAGE_CONTENT_BYTES / 2 + 1), "y".repeat(MAX_CORE_EVENT_DELTA_PAGE_CONTENT_BYTES / 2), ]); - assert!(too_much_content - .validate() - .unwrap_err() + let too_much_content_error = too_much_content.validate().unwrap_err(); + assert!(too_much_content_error .message .contains("selected-content byte bound")); + assert_eq!( + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope(too_much_content.pages.iter()) + .unwrap_err(), + too_much_content_error + ); let wire_expansion_bytes = MAX_CORE_EVENT_DELTA_PAGE_WIRE_BYTES / 6 + 1; assert!(wire_expansion_bytes < MAX_CORE_EVENT_DELTA_PAGE_CONTENT_BYTES); let too_many_wire_bytes = request(vec!["\0".repeat(wire_expansion_bytes)]); - assert!(too_many_wire_bytes - .validate() - .unwrap_err() + let too_many_wire_bytes_error = too_many_wire_bytes.validate().unwrap_err(); + assert!(too_many_wire_bytes_error .message .contains("page exceeds its wire bound")); + assert_eq!( + ApplyCoreEventDeltaPagesRequest::validate_prepared_envelope( + too_many_wire_bytes.pages.iter() + ) + .unwrap_err(), + too_many_wire_bytes_error + ); } #[test] From 325fe0273895d05267bf459bc08ba4be8f0b7856 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 07:35:49 -0400 Subject: [PATCH 08/12] fix(protocol): accept Pro graph manifest schema v4 --- .../generate_protocol_inventory/messages.rs | 2 +- crates/ctx-pro-host-protocol/src/message.rs | 13 +++++--- crates/ctx-pro-host-protocol/src/tests.rs | 31 ++++++++++++++++++- .../testdata/v3/inventory.json | 2 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs index 3a83ae749..9e4a888dc 100644 --- a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs +++ b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/messages.rs @@ -177,7 +177,7 @@ fn helper_messages(fingerprint: &str) -> Vec<(&'static str, HelperMessage)> { available_operations: operations(), finalization_progress: None, storage_evidence: Some(ProStorageEvidence { - graph_manifest_schema: 3, + graph_manifest_schema: 4, flat_format_version: 2, materializer_checkpoint_version: 8, journal_pack_format_version: 3, diff --git a/crates/ctx-pro-host-protocol/src/message.rs b/crates/ctx-pro-host-protocol/src/message.rs index 91d07645a..e393895d5 100644 --- a/crates/ctx-pro-host-protocol/src/message.rs +++ b/crates/ctx-pro-host-protocol/src/message.rs @@ -473,11 +473,14 @@ pub struct ProStorageEvidence { impl ProStorageEvidence { pub fn validate(&self) -> Result<(), ProtocolError> { - // V4 through V7 remain admissible only as authenticated status - // evidence while a helper control-loads a prior active graph for - // migration. V8 is current publication evidence; accepting a version - // here grants no EventIndex read compatibility between generations. - if self.graph_manifest_schema != 3 + // Graph manifest V3 remains admissible as authenticated compatibility + // evidence; V4 is current publication evidence. + // Materializer checkpoints V4 through V7 remain admissible only as + // authenticated status evidence while a helper control-loads a prior + // active graph for migration. Checkpoint V8 is current publication + // evidence; accepting a version here grants no EventIndex read + // compatibility between generations. + if !matches!(self.graph_manifest_schema, 3 | 4) || self.flat_format_version != 2 || !matches!(self.materializer_checkpoint_version, 4 | 5 | 6 | 7 | 8) || self.journal_pack_format_version != 3 diff --git a/crates/ctx-pro-host-protocol/src/tests.rs b/crates/ctx-pro-host-protocol/src/tests.rs index 5b2019a5a..aa3cefe30 100644 --- a/crates/ctx-pro-host-protocol/src/tests.rs +++ b/crates/ctx-pro-host-protocol/src/tests.rs @@ -549,7 +549,7 @@ fn storage_evidence_is_strict_and_required_nullable() { } #[test] -fn storage_evidence_accepts_v4_through_v8_only() { +fn storage_evidence_accepts_checkpoint_v4_through_v8_only() { let evidence = ProStorageEvidence { graph_manifest_schema: 3, flat_format_version: 2, @@ -584,3 +584,32 @@ fn storage_evidence_accepts_v4_through_v8_only() { assert_eq!(rejected.validate().unwrap_err().class, ErrorClass::Sequence); } } + +#[test] +fn storage_evidence_accepts_graph_manifest_v3_and_v4_only() { + let evidence = ProStorageEvidence { + graph_manifest_schema: 4, + flat_format_version: 2, + materializer_checkpoint_version: 8, + journal_pack_format_version: 3, + legacy_journals_written: 0, + journal_pages_written: 2, + journal_packs_written: 1, + journal_finish_activity: JournalFinishActivity { + worker_limit: 1, + peak_workers: 1, + started_after_preparation: true, + }, + }; + + for graph_manifest_schema in [3, 4] { + let mut accepted = evidence.clone(); + accepted.graph_manifest_schema = graph_manifest_schema; + accepted.validate().unwrap(); + } + for graph_manifest_schema in [0, 1, 2, 5, u16::MAX] { + let mut rejected = evidence.clone(); + rejected.graph_manifest_schema = graph_manifest_schema; + assert_eq!(rejected.validate().unwrap_err().class, ErrorClass::Sequence); + } +} diff --git a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json index 76d2885e4..268516490 100644 --- a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json +++ b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json @@ -1151,7 +1151,7 @@ "graph_key_deleted": "43545850524f0003000000817b2273657175656e6365223a332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c65746564222c22626f6479223a7b2264656c65746564223a747275657d7d7d", "graph_key_deletion_prepared": "43545850524f0003000000e97b2273657175656e6365223a322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c6574696f6e5f7072657061726564222c22626f6479223a7b226368616c6c656e67655f62617365363475726c223a2242675947426759474267594742675947426759474267594742675947426759474267594742675947426759222c22657870697265735f61745f756e6978223a3230302c226b65795f70726573656e74223a747275657d7d7d", "hello": "43545850524f0003000001bd7b2273657175656e6365223a302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2268656c6c6f222c22626f6479223a7b2270726f746f636f6c5f76657273696f6e223a332c2270726f746f636f6c5f66696e6765727072696e74223a2230616233633434356262356237643433336665353635316131643364396131306566373734643537306231333266353065656231343566333033343163376134222c2268656c7065725f76657273696f6e223a22676f6c64656e2d68656c706572222c226361706162696c6974696573223a5b22656e7469746c656d656e745f617574686f72697a6174696f6e222c2267726170685f6b65795f64656c6574696f6e222c22737461747573222c22636f72655f6d6174657269616c697a6174696f6e222c227175657279222c226769745f72656164225d2c22617574686f72697a6174696f6e5f6368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151227d7d7d", - "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a332c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a382c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" + "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a342c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a382c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" }, "host_frames": { "apply_core_event_delta_page": "43545850524f0003000011867b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d7d7d7d", From e8456865c39cc4957b24e25067e1c47fc6f54561 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 08:57:47 -0400 Subject: [PATCH 09/12] perf(index): binary search manifest sources for event pages --- .../ctx-history-index/src/query/execution.rs | 207 +++++++++++++++++- .../src/tests/query/source_pages.rs | 33 +++ 2 files changed, 235 insertions(+), 5 deletions(-) diff --git a/crates/ctx-history-index/src/query/execution.rs b/crates/ctx-history-index/src/query/execution.rs index 2bf47d9ec..4f9439e34 100644 --- a/crates/ctx-history-index/src/query/execution.rs +++ b/crates/ctx-history-index/src/query/execution.rs @@ -6,6 +6,34 @@ mod pages; mod search; mod sessions; +#[cfg(test)] +std::thread_local! { + static MANIFEST_SOURCE_IDENTITY_COMPARISONS: std::cell::Cell = + const { std::cell::Cell::new(0) }; +} + +fn retained_manifest_source_by_identity<'a>( + sources: &'a [ctx_history_core::CertifiedSource], + source: &SourceKey, +) -> Option<&'a ctx_history_core::CertifiedSource> { + let identity_digest = source.identity().digest(); + let index = sources + .binary_search_by(|candidate| { + #[cfg(test)] + MANIFEST_SOURCE_IDENTITY_COMPARISONS.with(|comparisons| { + comparisons.set(comparisons.get().saturating_add(1)); + }); + candidate + .observation() + .source() + .identity() + .digest() + .cmp(&identity_digest) + }) + .ok()?; + sources.get(index) +} + impl VerifiedIndex { /// Counts distinct live session identities from the merged Tantivy term /// dictionaries without reading stored event bodies. @@ -128,11 +156,7 @@ impl VerifiedIndex { } fn validate_source_event_source(&self, source: &SourceKey) -> Result<()> { - let retained = self - .manifest - .sources - .iter() - .find(|candidate| candidate.observation().source() == source) + let retained = retained_manifest_source_by_identity(&self.manifest.sources, source) .ok_or_else(|| { IndexError::SourceEventSourceNotRetained(source.identity().to_string()) })?; @@ -595,3 +619,176 @@ pub(super) fn core_event_page_budget_admits( .checked_add(candidate_content_bytes) .is_some_and(|total| total <= budget.maximum_content_bytes) } + +#[cfg(test)] +mod manifest_source_lookup_tests { + use std::{hint::black_box, time::Instant}; + + use ctx_history_core::{ + CertifiedSource, ScannedSourceCounts, SourceAnchor, SourceObservation, TypedKey, + }; + + use super::*; + + const FULL_CORPUS_SOURCE_COUNT: usize = 5_916; + const SCALED_SOURCE_COUNT: usize = FULL_CORPUS_SOURCE_COUNT * 4; + + fn source(sequence: usize) -> SourceKey { + SourceKey::derive( + "codex", + "codex_session_jsonl", + "session", + 1, + SourceAnchor::provider_native( + "session-file", + TypedKey::utf8(format!("manifest-source-{sequence:05}.jsonl")).unwrap(), + ) + .unwrap(), + ) + .unwrap() + } + + fn certificate(source: SourceKey) -> CertifiedSource { + let observation = SourceObservation::new(source, "regular-file-v1", vec![1]).unwrap(); + CertifiedSource::certify( + observation.clone(), + observation, + "codex-parser-v1", + [1; 32], + ScannedSourceCounts { + complete_records: 1, + retained_records: 1, + indexed_documents: 1, + certified_bytes: 10, + ..ScannedSourceCounts::default() + }, + ) + .unwrap() + } + + fn sorted_manifest_sources(cardinality: usize) -> Vec { + let mut sources = (0..cardinality) + .map(|sequence| certificate(source(sequence))) + .collect::>(); + sources.sort_by_key(|candidate| candidate.observation().source().identity().digest()); + assert!(sources.windows(2).all(|pair| { + pair[0].observation().source().identity().digest() + < pair[1].observation().source().identity().digest() + })); + sources + } + + fn reset_comparisons() { + MANIFEST_SOURCE_IDENTITY_COMPARISONS.with(|comparisons| comparisons.set(0)); + } + + fn comparisons() -> usize { + MANIFEST_SOURCE_IDENTITY_COMPARISONS.with(std::cell::Cell::get) + } + + fn comparison_bound(cardinality: usize) -> usize { + (usize::BITS - (cardinality - 1).leading_zeros()) as usize + 1 + } + + fn lookup_comparisons(sources: &[CertifiedSource], target: &SourceKey) -> usize { + reset_comparisons(); + let retained = retained_manifest_source_by_identity(sources, target).unwrap(); + assert!(retained.observation().source().exact_descriptor_eq(target)); + comparisons() + } + + #[test] + fn manifest_source_identity_lookup_preserves_exact_descriptor_semantics() { + let sources = sorted_manifest_sources(31); + for index in [0, sources.len() / 2, sources.len() - 1] { + let target = sources[index].observation().source(); + assert!(lookup_comparisons(&sources, target) <= comparison_bound(sources.len())); + } + + let absent = source(usize::MAX); + reset_comparisons(); + assert!(retained_manifest_source_by_identity(&sources, &absent).is_none()); + assert!(comparisons() <= comparison_bound(sources.len())); + + let retained = sources[sources.len() / 2].observation().source(); + let changed_descriptor = SourceKey::derive( + retained.provider(), + "codex_prompt_history_jsonl", + retained.schema_variant(), + retained.provider_identity_version(), + retained.anchor().clone(), + ) + .unwrap(); + assert_eq!(changed_descriptor, *retained); + assert!(!changed_descriptor.exact_descriptor_eq(retained)); + let found = retained_manifest_source_by_identity(&sources, &changed_descriptor).unwrap(); + assert_eq!(found.observation().source(), retained); + assert!(!found + .observation() + .source() + .exact_descriptor_eq(&changed_descriptor)); + } + + #[test] + fn manifest_source_identity_lookup_is_logarithmic_at_full_corpus_cardinality() { + let full_corpus = sorted_manifest_sources(FULL_CORPUS_SOURCE_COUNT); + let full_corpus_target = full_corpus.last().unwrap().observation().source(); + let full_corpus_comparisons = lookup_comparisons(&full_corpus, full_corpus_target); + assert!(full_corpus_comparisons <= comparison_bound(FULL_CORPUS_SOURCE_COUNT)); + + let scaled = sorted_manifest_sources(SCALED_SOURCE_COUNT); + let scaled_target = scaled.last().unwrap().observation().source(); + let scaled_comparisons = lookup_comparisons(&scaled, scaled_target); + assert!(scaled_comparisons <= comparison_bound(SCALED_SOURCE_COUNT)); + assert!(scaled_comparisons <= full_corpus_comparisons + 2); + } + + #[test] + #[ignore = "focused linear/binary manifest-source lookup benchmark; invoke explicitly"] + fn source_event_page_manifest_validation_benchmark_report() { + const LOOKUPS: usize = 4_096; + + for cardinality in [FULL_CORPUS_SOURCE_COUNT, SCALED_SOURCE_COUNT] { + let sources = sorted_manifest_sources(cardinality); + let target = sources.last().unwrap().observation().source().clone(); + + let mut linear_comparisons = 0_usize; + let linear_started = Instant::now(); + for _ in 0..LOOKUPS { + let retained = sources + .iter() + .find(|candidate| { + linear_comparisons = linear_comparisons.saturating_add(1); + candidate.observation().source() == &target + }) + .unwrap(); + black_box(retained); + } + let linear_elapsed = linear_started.elapsed(); + + reset_comparisons(); + let binary_started = Instant::now(); + for _ in 0..LOOKUPS { + let retained = + retained_manifest_source_by_identity(black_box(&sources), black_box(&target)) + .unwrap(); + black_box(retained); + } + let binary_elapsed = binary_started.elapsed(); + let binary_comparisons = comparisons(); + + assert_eq!(linear_comparisons, cardinality * LOOKUPS); + assert!(binary_comparisons <= comparison_bound(cardinality) * LOOKUPS); + eprintln!( + "manifest_sources={cardinality} lookups={LOOKUPS} \ + linear_comparisons_per_lookup={} binary_comparisons_per_lookup={:.2} \ + linear_ns_per_lookup={} binary_ns_per_lookup={} elapsed_speedup={:.2}x", + linear_comparisons / LOOKUPS, + binary_comparisons as f64 / LOOKUPS as f64, + linear_elapsed.as_nanos() / LOOKUPS as u128, + binary_elapsed.as_nanos() / LOOKUPS as u128, + linear_elapsed.as_secs_f64() / binary_elapsed.as_secs_f64().max(f64::EPSILON), + ); + } + } +} diff --git a/crates/ctx-history-index/src/tests/query/source_pages.rs b/crates/ctx-history-index/src/tests/query/source_pages.rs index 1d31aee0e..ed2f065d0 100644 --- a/crates/ctx-history-index/src/tests/query/source_pages.rs +++ b/crates/ctx-history-index/src/tests/query/source_pages.rs @@ -177,6 +177,39 @@ fn source_event_pages_order_across_segments_isolate_and_do_not_duplicate() { )); } +#[test] +fn source_event_page_manifest_source_identity_boundaries() { + let temp = tempdir().unwrap(); + let mut sources = (0..9) + .map(|sequence| source(&format!("manifest-boundary-{sequence}.jsonl"))) + .collect::>(); + let mut writer = GenerationWriter::open(temp.path(), WriterOptions::default()) + .unwrap() + .into_writer() + .unwrap(); + for (sequence, source) in sources.iter().enumerate().rev() { + writer.begin_source(source.clone()).unwrap(); + writer + .add_core_record(document(source, sequence as u64 + 1, "boundary body")) + .unwrap(); + writer.certify_source(certificate(source, 1, 1)).unwrap(); + } + writer.commit(|_| true).unwrap(); + + sources.sort_by_key(|source| source.identity().digest()); + let index = VerifiedIndex::open(temp.path()).unwrap(); + for target in [ + &sources[0], + &sources[sources.len() / 2], + sources.last().unwrap(), + ] { + let page = index.source_event_page(target, None, 1).unwrap(); + assert!(page.terminal); + assert_eq!(page.items.len(), 1); + assert!(page.items[0].source.exact_descriptor_eq(target)); + } +} + #[test] fn stored_core_source_page_retains_canonical_round_trip_bytes() { let temp = tempdir().unwrap(); From bf1de3b94e46e985377191af327b8897fb68115a Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 09:01:29 -0400 Subject: [PATCH 10/12] Avoid duplicate Core feed record hashing --- .../core_materialization_feed.rs | 12 +- .../ordered_prefetch.rs | 26 +++- .../tests/materialization.rs | 19 +-- .../tests/prefetch.rs | 129 ++++++++++++++++++ .../src/core_materialization.rs | 3 +- .../src/core_materialization/tests.rs | 7 + .../src/core_materialization/validation.rs | 10 +- crates/ctx-pro-host-protocol/src/lib.rs | 4 +- 8 files changed, 183 insertions(+), 27 deletions(-) diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs index 272e2906c..9e80eab52 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs @@ -16,8 +16,13 @@ use ctx_history_index::{ GenerationManifest, GenerationRetentionLease, SourceEventCursor, StoredCoreRecordJson, VerifiedIndex, }; +#[cfg(test)] +use ctx_pro_host_protocol::{ + core_record_sha256, CoreMaterializationFinalizationPhase, + CoreMaterializationFinalizationProgress, CoreSourceRemoval, +}; use ctx_pro_host_protocol::{ - core_record_digests_from_encoded, ApplyCoreEventDeltaPagesRequest, + core_record_sha256_from_encoded, ApplyCoreEventDeltaPagesRequest, ApplyCoreSourceDeltaPageRequest, BeginCoreMaterializationRequest, Capability, ContinueCoreMaterializationRequest, CoreEventDelta, CoreEventDeltaPage, CoreEventReplacement, CoreEventState, CoreEventStatePage, CoreEventStatePageRequest, CoreEventTombstone, @@ -31,11 +36,6 @@ use ctx_pro_host_protocol::{ MAX_CORE_EVENT_STATE_PAGE_ITEMS, MAX_CORE_SOURCE_DELTA_PAGE_ITEMS, MAX_CORE_SOURCE_DELTA_PAGE_WIRE_BYTES, MAX_CORE_SOURCE_STATES, }; -#[cfg(test)] -use ctx_pro_host_protocol::{ - core_record_sha256, CoreMaterializationFinalizationPhase, - CoreMaterializationFinalizationProgress, CoreSourceRemoval, -}; use serde::Serialize; use sha2::{Digest as _, Sha256}; diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/ordered_prefetch.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/ordered_prefetch.rs index c04d3a480..a8026e1e7 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/ordered_prefetch.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/ordered_prefetch.rs @@ -36,6 +36,8 @@ pub(super) struct CorePrefetchInstrumentationSnapshot { pub(super) materialized_pages: usize, pub(super) decoded_records: usize, pub(super) decoded_record_bytes: usize, + pub(super) record_payload_sha256_traversals: usize, + pub(super) record_payload_sha256_bytes: usize, pub(super) encoded_credit_high_water_bytes: usize, pub(super) encoded_credit_final_bytes: usize, pub(super) cancelled_waits_or_sends: usize, @@ -51,6 +53,10 @@ pub(super) struct CorePrefetchInstrumentation { materialized_pages: AtomicUsize, decoded_records: AtomicUsize, decoded_record_bytes: AtomicUsize, + #[cfg(test)] + record_payload_sha256_traversals: AtomicUsize, + #[cfg(test)] + record_payload_sha256_bytes: AtomicUsize, cancelled_waits_or_sends: AtomicUsize, } @@ -88,6 +94,14 @@ impl CorePrefetchInstrumentation { .fetch_add(record_bytes, AtomicOrdering::Relaxed); } + #[cfg(test)] + pub(super) fn record_payload_sha256_traversed(&self, encoded_bytes: usize) { + self.record_payload_sha256_traversals + .fetch_add(1, AtomicOrdering::Relaxed); + self.record_payload_sha256_bytes + .fetch_add(encoded_bytes, AtomicOrdering::Relaxed); + } + fn cancelled(&self) { self.cancelled_waits_or_sends .fetch_add(1, AtomicOrdering::Relaxed); @@ -107,6 +121,12 @@ impl CorePrefetchInstrumentation { materialized_pages: self.materialized_pages.load(AtomicOrdering::Relaxed), decoded_records: self.decoded_records.load(AtomicOrdering::Relaxed), decoded_record_bytes: self.decoded_record_bytes.load(AtomicOrdering::Relaxed), + record_payload_sha256_traversals: self + .record_payload_sha256_traversals + .load(AtomicOrdering::Relaxed), + record_payload_sha256_bytes: self + .record_payload_sha256_bytes + .load(AtomicOrdering::Relaxed), encoded_credit_high_water_bytes, encoded_credit_final_bytes, cancelled_waits_or_sends: self.cancelled_waits_or_sends.load(AtomicOrdering::Relaxed), @@ -536,9 +556,9 @@ fn prepare_planned_current_page( .into_iter() .map(|item| { let encoded = item.stored_json.encoded_core_record()?; - let core_record_sha256 = core_record_digests_from_encoded(&item.core_record, encoded) - .map_err(|error| anyhow!("invalid_request: {}", error.message))? - .core_record_sha256; + #[cfg(test)] + instrumentation.record_payload_sha256_traversed(encoded.len()); + let core_record_sha256 = core_record_sha256_from_encoded(encoded); Ok(PreparedCurrentRecord { record: item.core_record, stored_json: PreparedCoreRecordJson::Stored(item.stored_json), diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs index 251e4b094..5bcae49f7 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs @@ -184,18 +184,10 @@ fn prepared_request_reuses_stored_core_json_and_matches_legacy_frames_byte_for_b let added_record = added.core_record.clone(); let replacement_record = replacement.core_record.clone(); let tombstoned_event_id = tombstoned.core_record.event_id; - let added_sha256 = core_record_digests_from_encoded( - &added.core_record, - added.stored_json.encoded_core_record().unwrap(), - ) - .unwrap() - .core_record_sha256; - let replacement_sha256 = core_record_digests_from_encoded( - &replacement.core_record, - replacement.stored_json.encoded_core_record().unwrap(), - ) - .unwrap() - .core_record_sha256; + let added_sha256 = + core_record_sha256_from_encoded(added.stored_json.encoded_core_record().unwrap()); + let replacement_sha256 = + core_record_sha256_from_encoded(replacement.stored_json.encoded_core_record().unwrap()); let prepared_deltas = vec![ PreparedEventDelta::added(PreparedCurrentRecord { record: added.core_record, @@ -936,6 +928,7 @@ fn producer_reads_only_pinned_core_records() { } assert!(source.contains("plan_core_source_event_page_with_budget")); assert!(source.contains("materialize_stored_core_source_event_page")); - assert!(source.contains("core_record_digests_from_encoded")); + assert!(source.contains("core_record_sha256_from_encoded")); + assert!(!source.contains("core_record_digests_from_encoded")); assert!(!source.contains("MaterializeCoreRecordPage")); } diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/prefetch.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/prefetch.rs index be860eb2e..f858fe9ab 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/prefetch.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/prefetch.rs @@ -197,6 +197,14 @@ fn ordered_prefetch_is_byte_exact_and_bounded_across_workers_and_skew() { assert!(sequential.encoded_credit_high_water_bytes <= CORE_PREFETCH_ENCODED_BYTE_BUDGET); assert_eq!(sequential.planned_pages, sequential.materialized_pages); assert_eq!(sequential.decoded_records, record_count); + assert_eq!( + sequential.record_payload_sha256_traversals, + sequential.decoded_records + ); + assert_eq!( + sequential.record_payload_sha256_bytes, + sequential.decoded_record_bytes + ); for parallelism in [2, 4, 8] { let (actual_bytes, actual_digest, prefetch) = @@ -217,6 +225,14 @@ fn ordered_prefetch_is_byte_exact_and_bounded_across_workers_and_skew() { prefetch.encoded_credit_high_water_bytes ); assert_eq!(prefetch.planned_pages, prefetch.materialized_pages); + assert_eq!( + prefetch.record_payload_sha256_traversals, prefetch.decoded_records, + "case={case:?} workers={parallelism}" + ); + assert_eq!( + prefetch.record_payload_sha256_bytes, prefetch.decoded_record_bytes, + "case={case:?} workers={parallelism}" + ); match case { PrefetchFixture::OversizedSingletonAmongNormalSources => { assert_eq!(prefetch.workers_launched, parallelism); @@ -509,3 +525,116 @@ fn ordered_core_host_prefetch_microbenchmark() { ); } } + +#[test] +#[ignore = "focused Core-feed record SHA traversal microbenchmark"] +fn core_feed_record_sha_traversal_microbenchmark() { + const RECORDS: usize = MAX_CORE_EVENT_DELTA_PAGE_ITEMS; + const BODY_BYTES: usize = CORE_PREFETCH_PAGE_ENCODED_BYTE_BUDGET / RECORDS; + const SAMPLES: usize = 7; + + #[derive(Clone, Copy)] + enum Mode { + LegacyCombined, + RecordOnly, + } + + fn run( + records: &[(ctx_history_core::CoreRecord, Vec)], + mode: Mode, + ) -> (Duration, CorePrefetchInstrumentationSnapshot, u8) { + let instrumentation = CorePrefetchInstrumentation::default(); + let mut checksum = 0_u8; + let started = Instant::now(); + for (record, encoded) in records { + let digest = match mode { + Mode::LegacyCombined => { + // The discarded leaf and retained record digest each traverse + // the complete encoded payload in the former feed path. + instrumentation.record_payload_sha256_traversed(encoded.len()); + instrumentation.record_payload_sha256_traversed(encoded.len()); + ctx_pro_host_protocol::core_record_digests_from_encoded(record, encoded) + .unwrap() + .core_record_sha256 + } + Mode::RecordOnly => { + instrumentation.record_payload_sha256_traversed(encoded.len()); + core_record_sha256_from_encoded(encoded) + } + }; + checksum ^= digest.as_bytes()[0]; + black_box(&digest); + } + (started.elapsed(), instrumentation.snapshot(0, 0), checksum) + } + + let source = source("record-sha-traversal-benchmark.jsonl"); + let records = (0..RECORDS) + .map(|index| { + let record = record( + &source, + u64::try_from(index + 1).unwrap(), + "h".repeat(BODY_BYTES), + ); + let encoded = record.encode_stored().unwrap(); + (record, encoded) + }) + .collect::>(); + let encoded_bytes = records + .iter() + .map(|(_, encoded)| encoded.len()) + .sum::(); + + black_box(run(&records, Mode::LegacyCombined)); + black_box(run(&records, Mode::RecordOnly)); + + let mut legacy_samples = Vec::with_capacity(SAMPLES); + let mut record_only_samples = Vec::with_capacity(SAMPLES); + for sample in 0..SAMPLES { + let modes = if sample % 2 == 0 { + [Mode::LegacyCombined, Mode::RecordOnly] + } else { + [Mode::RecordOnly, Mode::LegacyCombined] + }; + for mode in modes { + let (elapsed, snapshot, checksum) = run(&records, mode); + match mode { + Mode::LegacyCombined => { + assert_eq!(snapshot.record_payload_sha256_traversals, RECORDS * 2); + assert_eq!(snapshot.record_payload_sha256_bytes, encoded_bytes * 2); + legacy_samples.push((elapsed, checksum)); + } + Mode::RecordOnly => { + assert_eq!(snapshot.record_payload_sha256_traversals, RECORDS); + assert_eq!(snapshot.record_payload_sha256_bytes, encoded_bytes); + record_only_samples.push((elapsed, checksum)); + } + } + } + } + assert!(legacy_samples + .iter() + .zip(&record_only_samples) + .all(|((_, legacy), (_, record_only))| legacy == record_only)); + legacy_samples.sort_unstable_by_key(|sample| sample.0); + record_only_samples.sort_unstable_by_key(|sample| sample.0); + let legacy_median = legacy_samples[SAMPLES / 2].0; + let record_only_median = record_only_samples[SAMPLES / 2].0; + eprintln!( + "core_feed_record_sha records={RECORDS} encoded_bytes={encoded_bytes} legacy_traversals={} legacy_payload_bytes={} legacy_median_ms={:.3} record_only_traversals={} record_only_payload_bytes={} record_only_median_ms={:.3} legacy_samples_ms={:?} record_only_samples_ms={:?}", + RECORDS * 2, + encoded_bytes * 2, + legacy_median.as_secs_f64() * 1_000.0, + RECORDS, + encoded_bytes, + record_only_median.as_secs_f64() * 1_000.0, + legacy_samples + .iter() + .map(|sample| sample.0.as_secs_f64() * 1_000.0) + .collect::>(), + record_only_samples + .iter() + .map(|sample| sample.0.as_secs_f64() * 1_000.0) + .collect::>() + ); +} diff --git a/crates/ctx-pro-host-protocol/src/core_materialization.rs b/crates/ctx-pro-host-protocol/src/core_materialization.rs index dee5b474a..5e520a47a 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization.rs @@ -22,7 +22,8 @@ pub(crate) use validation::encoded_len; use validation::{canonical_sha256, compact_json_encoded_len}; pub use validation::{ core_materialization_id, core_record_digests, core_record_digests_from_encoded, - core_record_leaf_sha256, core_record_sha256, core_source_snapshot_sha256, CoreRecordDigests, + core_record_leaf_sha256, core_record_sha256, core_record_sha256_from_encoded, + core_source_snapshot_sha256, CoreRecordDigests, }; use validation::{ core_record_content_bytes, core_source_delta_exact_eq, invalid_contract, diff --git a/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs b/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs index 7beac6c80..a2b519b99 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization/tests.rs @@ -15,6 +15,7 @@ use ctx_history_core::{ SessionRelationshipKind, SourceAnchor, TypedKey, CORE_CONTENT_POLICY_REVISION, CORE_REPOSITORY_ASSOCIATION_POLICY_REVISION, }; +use sha2::{Digest as _, Sha256}; use uuid::Uuid; use super::*; @@ -186,12 +187,18 @@ fn added_and_replaced_deltas_expose_exact_record_to_leaf_helper() { let expected = core_record_digests(&record).unwrap(); let stored_json = record.encode_stored().unwrap(); let decoded = CoreRecord::decode_stored(&stored_json).unwrap(); + let independent_record_sha256 = format!("{:x}", Sha256::digest(&stored_json)); assert_eq!(decoded, record); assert_eq!(decoded.encode_stored().unwrap(), stored_json); assert_eq!( core_record_digests_from_encoded(&decoded, &stored_json).unwrap(), expected ); + assert_eq!( + core_record_sha256_from_encoded(&stored_json), + independent_record_sha256 + ); + assert_eq!(expected.core_record_sha256, independent_record_sha256); for digest in [ &expected.core_record_sha256, &expected.core_record_leaf_sha256, diff --git a/crates/ctx-pro-host-protocol/src/core_materialization/validation.rs b/crates/ctx-pro-host-protocol/src/core_materialization/validation.rs index 0ed513fc0..2825a7c22 100644 --- a/crates/ctx-pro-host-protocol/src/core_materialization/validation.rs +++ b/crates/ctx-pro-host-protocol/src/core_materialization/validation.rs @@ -32,7 +32,13 @@ pub struct CoreRecordDigests { pub fn core_record_sha256(record: &CoreRecord) -> Result { let encoded = encode_core_record(record)?; - Ok(hex_sha256(Sha256::digest(encoded))) + Ok(core_record_sha256_from_encoded(&encoded)) +} + +/// Computes only the canonical record-state SHA from exact validated stored +/// Core JSON, without re-encoding the record or computing its frozen leaf. +pub fn core_record_sha256_from_encoded(encoded: &[u8]) -> String { + hex_sha256(Sha256::digest(encoded)) } /// Returns the frozen Core-record leaf while the exact `CoreRecord` is still @@ -59,7 +65,7 @@ pub fn core_record_digests_from_encoded( encoded: &[u8], ) -> Result { Ok(CoreRecordDigests { - core_record_sha256: hex_sha256(Sha256::digest(encoded)), + core_record_sha256: core_record_sha256_from_encoded(encoded), core_record_leaf_sha256: hex_sha256(core_record_leaf_digest(record, encoded)?), }) } diff --git a/crates/ctx-pro-host-protocol/src/lib.rs b/crates/ctx-pro-host-protocol/src/lib.rs index 58a2f7af7..004570448 100644 --- a/crates/ctx-pro-host-protocol/src/lib.rs +++ b/crates/ctx-pro-host-protocol/src/lib.rs @@ -90,8 +90,8 @@ pub use query_lineage::{ mod core_materialization; pub use core_materialization::{ core_materialization_id, core_record_digests, core_record_digests_from_encoded, - core_record_leaf_sha256, core_record_sha256, core_source_snapshot_sha256, - ApplyCoreEventDeltaPageRequest, ApplyCoreEventDeltaPagesRequest, + core_record_leaf_sha256, core_record_sha256, core_record_sha256_from_encoded, + core_source_snapshot_sha256, ApplyCoreEventDeltaPageRequest, ApplyCoreEventDeltaPagesRequest, ApplyCoreSourceDeltaPageRequest, BeginCoreMaterializationRequest, ContinueCoreMaterializationRequest, CoreEventDelta, CoreEventDeltaPage, CoreEventDeltaPageAcknowledgementIdentity, CoreEventDeltaPageApplied, From abf6ddff57a145a4c5b360e652e7482a6e072618 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 09:06:21 -0400 Subject: [PATCH 11/12] fix(pro): isolate terminal source sentinel --- .../core_materialization_feed.rs | 70 ++++++--- .../core_materialization_feed/tests.rs | 10 ++ .../tests/batching_replay.rs | 66 ++++++-- .../tests/finalization.rs | 17 +- .../tests/materialization.rs | 7 +- .../tests/proportionality.rs | 147 ++++++++++++++++++ 6 files changed, 271 insertions(+), 46 deletions(-) diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs index 9e80eab52..86b9b0dea 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed.rs @@ -1042,6 +1042,8 @@ fn core_snapshot_deltas(sources: &[CoreSourceState]) -> Vec { enum CoreSourceDeltaPageBuildError { #[error("invalid_request: one Core source delta exceeds its wire bound")] OversizedSingleton, + #[error("invalid_request: Core source terminal sentinel exceeds its wire bound")] + OversizedTerminalSentinel, #[error("invalid_request: Core delta page index overflowed")] PageIndexOverflow, #[error("invalid_request: Core source delta page byte accounting overflowed")] @@ -1072,9 +1074,13 @@ fn build_delta_pages_with_wire_bound( maximum_wire_bytes: usize, ) -> Result, CoreSourceDeltaPageBuildError> { if deltas.is_empty() { - return CoreSourceDeltaPage::new(materialization_id, generation_id, 0, true, Vec::new()) - .map(|page| vec![page]) - .map_err(|error| CoreSourceDeltaPageBuildError::InvalidPage(error.message)); + return terminal_source_delta_page( + materialization_id, + generation_id, + 0, + maximum_wire_bytes, + ) + .map(|page| vec![page]); } let mut pages = Vec::new(); @@ -1083,24 +1089,20 @@ fn build_delta_pages_with_wire_bound( let mut encoded_delta_items_bytes = 0_usize; let mut empty_nonterminal_wire_bytes = empty_source_delta_page_wire_bytes(materialization_id, generation_id, page_index, false)?; - let mut remaining = deltas.into_iter().peekable(); + let remaining = deltas.into_iter(); // A populated page is exactly its empty envelope plus each independently // encoded delta and the intervening commas. Charge every delta once, but - // rebuild the tiny envelope whenever page_index or terminal changes. - while let Some(delta) = remaining.next() { - let terminal = remaining.peek().is_none(); + // rebuild the tiny envelope whenever page_index changes. Populated source + // pages are always nonterminal; one empty terminal page follows them so + // paged removal acknowledgements never replay source data. + for delta in remaining { let encoded_delta_bytes = encoded_json_len(&delta)?; let candidate_delta_items_bytes = encoded_delta_items_bytes .checked_add(usize::from(!current.is_empty())) .and_then(|bytes| bytes.checked_add(encoded_delta_bytes)) .ok_or(CoreSourceDeltaPageBuildError::ByteCountOverflow)?; - let empty_wire_bytes = if terminal { - empty_source_delta_page_wire_bytes(materialization_id, generation_id, page_index, true)? - } else { - empty_nonterminal_wire_bytes - }; - let candidate_wire_bytes = empty_wire_bytes + let candidate_wire_bytes = empty_nonterminal_wire_bytes .checked_add(candidate_delta_items_bytes) .ok_or(CoreSourceDeltaPageBuildError::ByteCountOverflow)?; @@ -1129,17 +1131,7 @@ fn build_delta_pages_with_wire_bound( page_index, false, )?; - let singleton_empty_wire_bytes = if terminal { - empty_source_delta_page_wire_bytes( - materialization_id, - generation_id, - page_index, - true, - )? - } else { - empty_nonterminal_wire_bytes - }; - if singleton_empty_wire_bytes + if empty_nonterminal_wire_bytes .checked_add(encoded_delta_bytes) .is_none_or(|bytes| bytes > maximum_wire_bytes) { @@ -1157,12 +1149,40 @@ fn build_delta_pages_with_wire_bound( materialization_id, generation_id, page_index, - true, + false, current, )?); + page_index = page_index + .checked_add(1) + .ok_or(CoreSourceDeltaPageBuildError::PageIndexOverflow)?; + pages.push(terminal_source_delta_page( + materialization_id, + generation_id, + page_index, + maximum_wire_bytes, + )?); Ok(pages) } +fn terminal_source_delta_page( + materialization_id: &str, + generation_id: &str, + page_index: u32, + maximum_wire_bytes: usize, +) -> Result { + let page = validated_source_delta_page( + materialization_id, + generation_id, + page_index, + true, + Vec::new(), + )?; + if encoded_json_len(&page)? > maximum_wire_bytes { + return Err(CoreSourceDeltaPageBuildError::OversizedTerminalSentinel); + } + Ok(page) +} + fn validated_source_delta_page( materialization_id: &str, generation_id: &str, diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests.rs index 1e5878af8..d7389f9bf 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests.rs @@ -234,6 +234,8 @@ struct Consumer { source_response_mutation: Option, wrong_event_page_index: bool, source_exchanges: u64, + source_request_data_item_visits: u64, + source_request_bytes: u64, source_page_applications: u64, source_feed_terminal: bool, status_generation_override: Option, @@ -417,6 +419,14 @@ impl CoreMaterializationConsumer for Consumer { request: ApplyCoreSourceDeltaPageRequest, ) -> Result { request.validate().map_err(|error| anyhow!(error.message))?; + self.source_request_data_item_visits = self + .source_request_data_item_visits + .checked_add(u64::try_from(request.page.deltas.len()).unwrap()) + .unwrap(); + self.source_request_bytes = self + .source_request_bytes + .checked_add(u64::try_from(serde_json::to_vec(&request).unwrap().len()).unwrap()) + .unwrap(); let page = request.page; assert!(serde_json::to_vec(&page).unwrap().len() <= MAX_CORE_SOURCE_DELTA_PAGE_WIRE_BYTES); let acknowledgement_page_index = request.acknowledgement_page_index; diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/batching_replay.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/batching_replay.rs index 7ebe98504..d40e3c092 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/batching_replay.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/batching_replay.rs @@ -353,21 +353,20 @@ fn source_page_admission_uses_exact_wire_index_at_decimal_transitions() { ); assert_eq!( pages[usize::try_from(transition + 2).unwrap()].deltas.len(), - 2 + 1 ); assert_eq!( - serde_json::to_vec(&pages[usize::try_from(transition + 2).unwrap()]) - .unwrap() - .len(), - maximum_wire_bytes + pages[usize::try_from(transition + 3).unwrap()].deltas.len(), + 1 ); assert_eq!( pages.last().map(|page| page.page_index), - Some(transition + 2) + Some(transition + 4) ); for (expected_index, page) in pages.iter().enumerate() { assert_eq!(page.page_index, u32::try_from(expected_index).unwrap()); assert_eq!(page.terminal, expected_index + 1 == pages.len()); + assert_eq!(page.deltas.is_empty(), page.terminal); assert!(serde_json::to_vec(page).unwrap().len() <= maximum_wire_bytes); page.validate().unwrap(); } @@ -382,15 +381,20 @@ fn source_page_admission_enforces_the_protocol_wire_bound() { assert_eq!(MAX_CORE_SOURCE_DELTA_PAGE_WIRE_BYTES, 4_194_304); let pages = build_delta_pages(&materialization_id, &generation_id, deltas).unwrap(); + let (terminal, data_pages) = pages.split_last().unwrap(); - assert!(pages.len() > 1); - for (index, page) in pages.iter().enumerate() { + assert!(data_pages.len() > 1); + assert!(terminal.terminal); + assert!(terminal.deltas.is_empty()); + terminal.validate().unwrap(); + for (index, page) in data_pages.iter().enumerate() { let wire_bytes = serde_json::to_vec(page).unwrap().len(); assert!(wire_bytes <= MAX_CORE_SOURCE_DELTA_PAGE_WIRE_BYTES); assert_eq!(page.page_index, u32::try_from(index).unwrap()); - assert_eq!(page.terminal, index + 1 == pages.len()); + assert!(!page.terminal); + assert!(!page.deltas.is_empty()); page.validate().unwrap(); - if let Some(next) = pages.get(index + 1) { + if let Some(next) = data_pages.get(index + 1) { let next_delta_bytes = serde_json::to_vec(&next.deltas[0]).unwrap().len(); assert!( page.deltas.len() == MAX_CORE_SOURCE_DELTA_PAGE_ITEMS @@ -410,7 +414,7 @@ fn source_page_admission_rejects_an_oversized_singleton_with_typed_error() { &materialization_id, &generation_id, 0, - true, + false, vec![delta.clone()], ) .unwrap(), @@ -436,6 +440,31 @@ fn source_page_admission_rejects_an_oversized_singleton_with_typed_error() { ); } +#[test] +fn source_page_admission_rejects_an_oversized_terminal_sentinel() { + let materialization_id = "d".repeat(64); + let generation_id = "a".repeat(64); + let terminal_bytes = serde_json::to_vec( + &CoreSourceDeltaPage::new(&materialization_id, &generation_id, 0, true, Vec::new()) + .unwrap(), + ) + .unwrap() + .len(); + + let error = build_delta_pages_with_wire_bound( + &materialization_id, + &generation_id, + Vec::new(), + terminal_bytes - 1, + ) + .unwrap_err(); + + assert!(matches!( + error, + CoreSourceDeltaPageBuildError::OversizedTerminalSentinel + )); +} + #[test] fn same_certificate_and_count_with_changed_core_record_is_reconciled() { let temp = tempdir().unwrap(); @@ -682,12 +711,12 @@ fn partial_materialization_resumes_from_source_page_zero_with_complete_finish_co consumer.replay_status_currentness = Some(CoreProjectionCurrentness::Partial); let report = sync_core_feed(&index, None, &mut consumer).unwrap(); - assert_eq!(consumer.delta_pages.len(), 1); assert_eq!(consumer.delta_pages[0], committed_page); - assert_eq!(consumer.source_page_applications, 1); + assert_eq!(consumer.delta_pages.len(), 2); + assert_eq!(consumer.source_page_applications, 2); assert_eq!( consumer.source_acknowledgement_requests, - vec![(0, 0), (0, 0)] + vec![(0, 0), (0, 0), (1, 0)] ); let acknowledgement = &consumer.source_acknowledgements[&0][0]; assert_eq!(acknowledgement.page_index, 0); @@ -700,7 +729,7 @@ fn partial_materialization_resumes_from_source_page_zero_with_complete_finish_co assert_eq!(report.event_delta_pages, 1); assert_eq!(report.event_mutations, 1); let finish = consumer.finish.as_ref().unwrap(); - assert_eq!(finish.source_delta_pages, 1); + assert_eq!(finish.source_delta_pages, 2); assert_eq!(finish.changed_sources, 1); assert_eq!(finish.event_delta_pages, 1); assert_eq!(finish.event_mutations, 1); @@ -868,7 +897,7 @@ fn partial_mixed_committed_prefix_and_new_suffix_is_singleton_bounded() { } #[test] -fn journaled_source_state_and_event_retries_are_exact_and_divergence_fails_closed() { +fn journaled_retries_are_exact_and_old_terminal_data_layout_fails_closed() { let (_temp, index) = single_source_index("exact-duplicate-retry.jsonl", vec!["body".to_owned()]); let mut consumer = Consumer::new(); @@ -882,8 +911,11 @@ fn journaled_source_state_and_event_retries_are_exact_and_divergence_fails_close }) .unwrap(); assert!(source_response.replayed); + assert!(!source_page.terminal); + assert!(!source_page.deltas.is_empty()); let mut divergent_source = source_page; - divergent_source.terminal = !divergent_source.terminal; + divergent_source.terminal = true; + divergent_source.validate().unwrap(); let error = consumer .apply_source_delta(ApplyCoreSourceDeltaPageRequest { page: divergent_source, diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs index 2884fc2cb..fbf3b06f9 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/finalization.rs @@ -17,12 +17,23 @@ fn finish_for(index: &VerifiedIndex, revision: &str) -> FinishCoreMaterializatio head: head.clone(), expected_prior_receipt: None, }; + let materialization_id = + ctx_pro_host_protocol::core_materialization_id(&begin, revision).unwrap(); + let source_delta_pages = u32::try_from( + build_delta_pages( + &materialization_id, + &head.core_generation_id, + core_snapshot_deltas(&sources), + ) + .unwrap() + .len(), + ) + .unwrap(); FinishCoreMaterializationRequest { - materialization_id: ctx_pro_host_protocol::core_materialization_id(&begin, revision) - .unwrap(), + materialization_id, head, expected_prior_receipt: None, - source_delta_pages: 1, + source_delta_pages, changed_sources: 1, removed_sources: 0, event_delta_pages: 1, diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs index 5bcae49f7..95f8e422b 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/materialization.rs @@ -694,6 +694,9 @@ fn large_native_key_removals_without_receipt_drive_all_acknowledgements_idempote u64::try_from(REMOVAL_COUNT).unwrap() ); assert_eq!(consumer.source_page_applications, 1); + assert_eq!(consumer.delta_pages.len(), 1); + assert!(consumer.delta_pages[0].terminal); + assert!(consumer.delta_pages[0].deltas.is_empty()); assert_eq!( consumer.finish.as_ref().unwrap().removed_sources, u32::try_from(REMOVAL_COUNT).unwrap() @@ -756,6 +759,8 @@ fn large_native_key_removals_without_receipt_drive_all_acknowledgements_idempote } let source_page = consumer.delta_pages[0].clone(); + assert!(source_page.terminal); + assert!(source_page.deltas.is_empty()); let original_responses = responses.clone(); for (index, expected) in original_responses.into_iter().enumerate() { let replayed = consumer @@ -891,7 +896,7 @@ fn source_acknowledgement_sequence_and_global_identity_fail_closed() { (SourceResponseMutation::StalePresent, "stale current source"), ( SourceResponseMutation::RemoveCurrent, - "removes a current source", + "stored-minus-snapshot removals are valid only on the terminal source page", ), ( SourceResponseMutation::SkipMaterializeIndex, diff --git a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/proportionality.rs b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/proportionality.rs index df0e6a3ab..70c364336 100644 --- a/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/proportionality.rs +++ b/crates/ctx-cli/src/pro/client_output/core_materialization_feed/tests/proportionality.rs @@ -65,6 +65,153 @@ fn sync_serial( .unwrap() } +#[derive(Debug, Default, PartialEq, Eq)] +struct SourceRequestMetrics { + data_item_visits: u64, + request_bytes: u64, +} + +fn source_request_metrics( + pages: &[CoreSourceDeltaPage], + terminal_acknowledgement_pages: usize, +) -> SourceRequestMetrics { + let mut metrics = SourceRequestMetrics::default(); + for (page_position, page) in pages.iter().enumerate() { + let request_count = if page_position + 1 == pages.len() { + terminal_acknowledgement_pages + } else { + 1 + }; + for acknowledgement_page_index in 0..request_count { + let request = ApplyCoreSourceDeltaPageRequest { + page: page.clone(), + acknowledgement_page_index: u32::try_from(acknowledgement_page_index).unwrap(), + }; + request.validate().unwrap(); + metrics.data_item_visits = metrics + .data_item_visits + .checked_add(u64::try_from(page.deltas.len()).unwrap()) + .unwrap(); + metrics.request_bytes = metrics + .request_bytes + .checked_add(u64::try_from(serde_json::to_vec(&request).unwrap().len()).unwrap()) + .unwrap(); + } + } + metrics +} + +#[test] +fn source_page_feed_is_linear_for_5916_current_sources_and_maximum_prior_removals() { + const CURRENT_SOURCE_COUNT: usize = 5_916; + const PRIOR_REMOVAL_COUNT: usize = MAX_CORE_SOURCE_STATES; + + let materialization_id = "d".repeat(64); + let generation_id = "a".repeat(64); + let deltas = ordered_source_deltas(CURRENT_SOURCE_COUNT, 32); + let pages = build_delta_pages(&materialization_id, &generation_id, deltas).unwrap(); + let (terminal_page, data_pages) = pages.split_last().unwrap(); + assert_eq!( + data_pages.len(), + CURRENT_SOURCE_COUNT.div_ceil(MAX_CORE_SOURCE_DELTA_PAGE_ITEMS) + ); + assert!(data_pages + .iter() + .all(|page| !page.terminal && !page.deltas.is_empty())); + assert!(terminal_page.terminal); + assert!(terminal_page.deltas.is_empty()); + + let mut consumer = Consumer::new(); + for source_index in 0..PRIOR_REMOVAL_COUNT { + let removed = source(&format!("prior-removal-{source_index:05}.jsonl")); + assert!(consumer + .known_sources + .insert(removed.identity().digest(), removed) + .is_none()); + } + + let mut changed_sources = 0_u64; + let mut removed_sources = 0_u64; + for page in pages.clone() { + let mut acknowledgement_page_index = 0_u32; + loop { + let request = ApplyCoreSourceDeltaPageRequest { + page: page.clone(), + acknowledgement_page_index, + }; + let identity = request.acknowledgement_identity(); + let applied = consumer.apply_source_delta(request).unwrap(); + applied.validate_for_identity(&identity).unwrap(); + changed_sources = changed_sources + .checked_add(u64::from(applied.changed_sources)) + .unwrap(); + removed_sources = removed_sources + .checked_add(u64::from(applied.removed_sources)) + .unwrap(); + if applied.acknowledgement_terminal { + break; + } + acknowledgement_page_index = acknowledgement_page_index.checked_add(1).unwrap(); + } + } + + let removal_acknowledgement_pages = + PRIOR_REMOVAL_COUNT.div_ceil(MAX_CORE_SOURCE_DELTA_PAGE_ITEMS); + let mut expected_requests = data_pages + .iter() + .map(|page| (page.page_index, 0)) + .collect::>(); + expected_requests.extend( + (0..removal_acknowledgement_pages) + .map(|index| (terminal_page.page_index, u32::try_from(index).unwrap())), + ); + + assert_eq!( + changed_sources, + u64::try_from(CURRENT_SOURCE_COUNT).unwrap() + ); + assert_eq!(removed_sources, u64::try_from(PRIOR_REMOVAL_COUNT).unwrap()); + assert_eq!(consumer.source_acknowledgement_requests, expected_requests); + assert_eq!(consumer.delta_pages, pages); + assert_eq!( + consumer.source_page_applications, + u64::try_from(pages.len()).unwrap() + ); + assert_eq!( + consumer.source_request_data_item_visits, + u64::try_from(CURRENT_SOURCE_COUNT).unwrap() + ); + + // Measurement-only reconstruction of the prior public layout. It is not a + // compatibility path: the final populated page carried `terminal: true` + // and was resent for every acknowledgement page containing its changed + // sources plus all prior removals. + let mut old_layout_pages = data_pages.to_vec(); + let old_terminal_page = old_layout_pages.last_mut().unwrap(); + old_terminal_page.terminal = true; + old_terminal_page.validate().unwrap(); + let old_terminal_acknowledgement_pages = PRIOR_REMOVAL_COUNT + .checked_add(old_terminal_page.deltas.len()) + .unwrap() + .div_ceil(MAX_CORE_SOURCE_DELTA_PAGE_ITEMS); + let before = source_request_metrics(&old_layout_pages, old_terminal_acknowledgement_pages); + let after = SourceRequestMetrics { + data_item_visits: consumer.source_request_data_item_visits, + request_bytes: consumer.source_request_bytes, + }; + + assert_eq!(before.data_item_visits, 7_708); + assert_eq!(after.data_item_visits, 5_916); + assert!(after.request_bytes < before.request_bytes); + eprintln!( + "source_page_proportionality current_sources={CURRENT_SOURCE_COUNT} prior_removals={PRIOR_REMOVAL_COUNT} before_data_item_visits={} after_data_item_visits={} before_request_bytes={} after_request_bytes={} old_terminal_acknowledgements={old_terminal_acknowledgement_pages} new_terminal_acknowledgements={removal_acknowledgement_pages}", + before.data_item_visits, + after.data_item_visits, + before.request_bytes, + after.request_bytes, + ); +} + #[test] fn changed_logical_source_replay_work_is_measured_for_jsonl_and_sqlite() { for (source_format, source_name) in [ From d2577a2d80897641e310cf8f58309a907ac7af22 Mon Sep 17 00:00:00 2001 From: luca-ctx <216224554+luca-ctx@users.noreply.github.com> Date: Sat, 8 Aug 2026 10:18:16 -0400 Subject: [PATCH 12/12] fix(protocol): bind golden source state to record content --- .../generate_protocol_inventory/fixtures.rs | 44 ++++++++++++------- .../testdata/v3/inventory.json | 30 ++++++------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/fixtures.rs b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/fixtures.rs index 88565fc0a..43c980e75 100644 --- a/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/fixtures.rs +++ b/crates/ctx-pro-host-protocol/examples/generate_protocol_inventory/fixtures.rs @@ -1,10 +1,11 @@ use super::*; use ctx_history_core::{ - core_record_contract_fingerprint, derive_event_id, derive_session_id, CoreContent, - CoreContentPolicyStatus, CoreDiscoveryExclusion, EventIdentityInput, EventOrigin, - NativeItemKey, NativeSessionKey, RepositoryCandidateEvidence, RepositoryCandidateKind, - SessionIdentityInput, SessionRelationshipKind, SourceAnchor, TypedKey, - CORE_CONTENT_POLICY_REVISION, CORE_NORMALIZATION_REVISION, CORE_RECORD_VERSION, + core_record_accumulator_leaf_digest, core_record_contract_fingerprint, core_record_leaf_digest, + derive_event_id, derive_session_id, CoreContent, CoreContentPolicyStatus, + CoreDiscoveryExclusion, EventIdentityInput, EventOrigin, NativeItemKey, NativeSessionKey, + RepositoryCandidateEvidence, RepositoryCandidateKind, SessionIdentityInput, + SessionRelationshipKind, SourceAnchor, TypedKey, CORE_CONTENT_POLICY_REVISION, + CORE_NORMALIZATION_REVISION, CORE_RECORD_VERSION, }; pub(super) fn source(lineage: u8) -> SourceKey { @@ -18,20 +19,12 @@ pub(super) fn source(lineage: u8) -> SourceKey { .expect("golden source") } -pub(super) fn source_state(lineage: u8, revision: u8, event_count: u64) -> CoreSourceState { - CoreSourceState { - source: source(lineage), - core_record_accumulator: format!("{revision:064x}"), - event_count, - } -} - pub(super) fn source_removal() -> CoreSourceRemoval { CoreSourceRemoval { source: source(2) } } pub(super) fn source_deltas() -> Vec { - let mut deltas = vec![CoreSourceDelta::Present(source_state(1, 1, 1))]; + let mut deltas = vec![CoreSourceDelta::Present(golden_source_state())]; deltas.sort_by_key(|delta| delta.source().identity().digest()); deltas } @@ -45,7 +38,7 @@ pub(super) fn head() -> CoreGenerationHead { 3, 2, "c".repeat(64), - &[source_state(1, 1, 1)], + &[golden_source_state()], ) .expect("golden head") } @@ -152,10 +145,29 @@ pub(super) fn record() -> CoreRecord { } } +fn golden_source_state() -> CoreSourceState { + let record = record(); + let encoded = record.encode_stored().expect("golden Core record encoding"); + let leaf = core_record_leaf_digest(record.event_id, &encoded).expect("golden Core record leaf"); + let accumulator = core_record_accumulator_leaf_digest(record.event_id, &leaf) + .expect("golden Core record accumulator"); + const HEX: &[u8; 16] = b"0123456789abcdef"; + let mut encoded_accumulator = String::with_capacity(64); + for byte in accumulator { + encoded_accumulator.push(HEX[usize::from(byte >> 4)] as char); + encoded_accumulator.push(HEX[usize::from(byte & 0x0f)] as char); + } + CoreSourceState { + source: source(1), + core_record_accumulator: encoded_accumulator, + event_count: 1, + } +} + pub(super) fn reconciliation() -> CoreSourceReconciliation { CoreSourceReconciliation { materialize_index: 0, - delta: CoreSourceDelta::Present(source_state(1, 1, 1)), + delta: CoreSourceDelta::Present(golden_source_state()), } } diff --git a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json index 268516490..3a6f5dd87 100644 --- a/crates/ctx-pro-host-protocol/testdata/v3/inventory.json +++ b/crates/ctx-pro-host-protocol/testdata/v3/inventory.json @@ -1140,30 +1140,30 @@ "helper_frames": { "authorized": "43545850524f0003000000fd7b2273657175656e6365223a312c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22617574686f72697a6564222c22626f6479223a7b227374617465223a22747269616c222c22726566726573685f7265717569726564223a66616c73652c22657870697265735f61745f756e6978223a3137352c226163636573735f646561646c696e655f756e6978223a3230302c2267726163655f646561646c696e655f756e6978223a3235302c226361706162696c6974696573223a5b2267726170685f72656164225d7d7d7d", "blame": "43545850524f0003000011897b2273657175656e6365223a31312c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22626c616d65222c22626f6479223a7b22736e617073686f74223a7b226b696e64223a22636f7265222c2272656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631227d7d2c22746172676574223a7b226b696e64223a22636f6d6d6974222c22636f6d6d6974223a7b226964223a22636f6d6d69743a676f6c64656e2d726573756c74222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c227265706f7369746f7279223a7b226964223a227265706f7369746f72793a63747872732d637478222c226b696e64223a227265706f7369746f7279222c22646973706c6179223a2263747872732f637478227d7d2c226769745f736e617073686f74223a6e756c6c2c226f7574636f6d65223a7b226174747269627574696f6e223a226e6f6e65222c22636f766572616765223a7b22756e6974223a22636f6d6d69745f66616374222c226576616c7561746564223a302c2270726f76656e223a302c22706f737369626c65223a302c22636f6e666c696374696e67223a302c226e6f6e65223a307d7d2c226d617463686573223a5b5d2c2265766964656e6365223a5b7b226e756d626572223a312c226369746174696f6e223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c226576656e745f73657175656e6365223a312c22627974655f72616e6765223a6e756c6c2c2265766964656e63655f736861323536223a6e756c6c7d7d5d2c226e657874223a6e756c6c2c226c696e65616765223a7b22726571756573746564223a7b227265736f75726365223a7b226964223a22636f6d6d69743a676f6c64656e2d726573756c74222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c226c6f676963616c5f7265706f7369746f72795f6964223a2263747872732f637478222c226f626a6563745f666f726d6174223a2273686131222c226f6964223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c226564676573223a5b7b226f7065726174696f6e5f6964223a2264646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464222c226b696e64223a22726562617365222c2272656c6174696f6e5f636c617373223a227265706c6163656d656e74222c22736f75726365223a7b227265736f75726365223a7b226964223a22636f6d6d69743a676f6c64656e2d736f75726365222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2231313131313131313131313131313131313131313131313131313131313131313131313131313131227d2c226c6f676963616c5f7265706f7369746f72795f6964223a2263747872732f637478222c226f626a6563745f666f726d6174223a2273686131222c226f6964223a2231313131313131313131313131313131313131313131313131313131313131313131313131313131227d2c22726573756c74223a7b227265736f75726365223a7b226964223a22636f6d6d69743a676f6c64656e2d726573756c74222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c226c6f676963616c5f7265706f7369746f72795f6964223a2263747872732f637478222c226f626a6563745f666f726d6174223a2273686131222c226f6964223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c226163746f72223a7b226964223a2273657373696f6e3a63353231636532662d616664302d386162662d616439642d643166343335346635313038222c226b696e64223a2273657373696f6e222c22646973706c6179223a22676f6c64656e2d73657373696f6e227d2c2270726f6f665f636c617373223a227265706f7369746f72795f7665726966696564222c227374617465223a226173736572746564222c226f627365727665645f61745f6d73223a313730303030303030303030302c2265766964656e63655f6e756d62657273223a5b315d7d5d2c227969656c6465645f6279223a5b5d2c226f726967696e223a7b227265736f75726365223a7b226964223a22636f6d6d69743a676f6c64656e2d736f75726365222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2231313131313131313131313131313131313131313131313131313131313131313131313131313131227d2c226c6f676963616c5f7265706f7369746f72795f6964223a2263747872732f637478222c226f626a6563745f666f726d6174223a2273686131222c226f6964223a2231313131313131313131313131313131313131313131313131313131313131313131313131313131227d2c22656e64706f696e74223a7b226b696e64223a2263757272656e745f61745f726566222c22636f6d6d6974223a7b227265736f75726365223a7b226964223a22636f6d6d69743a676f6c64656e2d726573756c74222c226b696e64223a22636f6d6d6974222c22646973706c6179223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c226c6f676963616c5f7265706f7369746f72795f6964223a2263747872732f637478222c226f626a6563745f666f726d6174223a2273686131222c226f6964223a2233333333333333333333333333333333333333333333333333333333333333333333333333333333227d2c2273636f7065223a7b226964223a226272616e63683a63747872732d6374783a6d61696e222c226b696e64223a226272616e6368222c22646973706c6179223a226d61696e227d2c226f62736572766174696f6e5f6964223a226f62736572766174696f6e3a676f6c64656e2d6d61696e222c226f627365727665645f61745f6d73223a313730303030303030303030302c2265766964656e63655f6e756d62657273223a5b315d7d2c22636f6d706c657465223a747275652c22616d626967756f7573223a66616c73652c22626f756e6473223a7b2272657475726e65645f6576656e7473223a312c2272657475726e65645f6576656e745f6c696d6974223a3130302c226578616d696e65645f6576656e7473223a312c226578616d696e65645f6576656e745f6c696d6974223a313030302c226f6d697373696f6e223a7b226b696e64223a226578616374222c22636f756e74223a307d2c227472756e636174696f6e5f726561736f6e223a6e756c6c7d7d7d7d7d", - "core_event_delta_page_applied": "43545850524f00030000042e7b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f64656c74615f706167655f6170706c696564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22706167655f696e646578223a302c226164646974696f6e73223a312c227265706c6163656d656e7473223a302c22746f6d6273746f6e6573223a302c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d7d7d", - "core_event_delta_pages_applied": "43545850524f00030000043c7b2273657175656e6365223a31332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f64656c74615f70616765735f6170706c696564222c22626f6479223a7b227061676573223a5b7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22706167655f696e646578223a302c226164646974696f6e73223a312c227265706c6163656d656e7473223a302c22746f6d6273746f6e6573223a302c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d5d7d7d7d", - "core_event_state_page": "43545850524f0003000004d57b2273657175656e6365223a372c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f73746174655f70616765222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c2261667465725f6576656e745f6964223a6e756c6c2c22737461746573223a5b5d2c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d7d7d", - "core_materialization_began": "43545850524f0003000001907b2273657175656e6365223a352c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f626567616e222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c2265787065637465645f7072696f725f72656365697074223a6e756c6c2c227265706c61796564223a66616c73657d7d7d", - "core_materialization_finalization_pending": "43545850524f0003000002517b2273657175656e6365223a31302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f66696e616c697a6174696f6e5f70656e64696e67222c22626f6479223a7b2270726f6772657373223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2266696e6973685f726571756573745f646967657374223a2261396532633164626534353963346135646161356634353735353033643036386634333830623837643539633163363530363633653663326339663062623531222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c227068617365223a22656d69745f666c6174222c22637572736f725f736861323536223a2265656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565227d2c227265706c61796564223a66616c73657d7d7d", - "core_materialization_finished": "43545850524f0003000002bf7b2273657175656e6365223a392c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f66696e6973686564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c2266696e6973685f726571756573745f646967657374223a2261396532633164626534353963346135646161356634353735353033643036386634333830623837643539633163363530363633653663326339663062623531222c2272656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c227265706c61796564223a66616c73657d7d7d", - "core_source_delta_page_applied": "43545850524f0003000007f37b2273657175656e6365223a362c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f736f757263655f64656c74615f706167655f6170706c696564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22706167655f696e646578223a302c2261636b6e6f776c656467656d656e745f706167655f696e646578223a302c2261636b6e6f776c656467656d656e745f7465726d696e616c223a747275652c226368616e6765645f736f7572636573223a312c2272656d6f7665645f736f7572636573223a312c227265636f6e63696c655f736f7572636573223a5b7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c7b226d6174657269616c697a655f696e646578223a312c2264656c7461223a7b226b696e64223a2272656d6f766564222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c325d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b32382c3136372c3132342c33352c3230332c3230322c3134312c3234322c32302c31332c3134332c3132322c32342c33362c3135312c3137392c34352c34372c3134332c38382c3133302c3232392c3132392c31352c3135372c38332c3130352c332c3235312c3133352c3135362c3137345d2c22736f757263655f646967657374223a5b32382c3136372c3132342c33352c3230332c3230322c3134312c3234322c32302c31332c3134332c3132322c32342c33362c3135312c3137392c34352c34372c3134332c38382c3133302c3232392c3132392c31352c3135372c38332c3130352c332c3235312c3133352c3135362c3137345d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2231636137376332332d636263612d386466322d393430642d386637613138323439376233227d7d7d7d7d5d2c227265706c61796564223a66616c73657d7d7d", + "core_event_delta_page_applied": "43545850524f00030000042e7b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f64656c74615f706167655f6170706c696564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22706167655f696e646578223a302c226164646974696f6e73223a312c227265706c6163656d656e7473223a302c22746f6d6273746f6e6573223a302c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d7d7d", + "core_event_delta_pages_applied": "43545850524f00030000043c7b2273657175656e6365223a31332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f64656c74615f70616765735f6170706c696564222c22626f6479223a7b227061676573223a5b7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22706167655f696e646578223a302c226164646974696f6e73223a312c227265706c6163656d656e7473223a302c22746f6d6273746f6e6573223a302c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d5d7d7d7d", + "core_event_state_page": "43545850524f0003000004d57b2273657175656e6365223a372c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f73746174655f70616765222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c2261667465725f6576656e745f6964223a6e756c6c2c22737461746573223a5b5d2c227465726d696e616c223a747275652c227265706c61796564223a66616c73657d7d7d", + "core_materialization_began": "43545850524f0003000001907b2273657175656e6365223a352c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f626567616e222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c2265787065637465645f7072696f725f72656365697074223a6e756c6c2c227265706c61796564223a66616c73657d7d7d", + "core_materialization_finalization_pending": "43545850524f0003000002517b2273657175656e6365223a31302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f66696e616c697a6174696f6e5f70656e64696e67222c22626f6479223a7b2270726f6772657373223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2266696e6973685f726571756573745f646967657374223a2235383235636233363266353238366332653330646332656666613564653162393364376437386363363433313365366533663434306639663238383636366366222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c227068617365223a22656d69745f666c6174222c22637572736f725f736861323536223a2265656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565227d2c227265706c61796564223a66616c73657d7d7d", + "core_materialization_finished": "43545850524f0003000002bf7b2273657175656e6365223a392c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6d6174657269616c697a6174696f6e5f66696e6973686564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c2266696e6973685f726571756573745f646967657374223a2235383235636233363266353238366332653330646332656666613564653162393364376437386363363433313365366533663434306639663238383636366366222c2272656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2261636661366334326262396637643739633331633932623664306266373030386461393536303139333439623032633130363261376234373631626664346139222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c227265706c61796564223a66616c73657d7d7d", + "core_source_delta_page_applied": "43545850524f0003000007f37b2273657175656e6365223a362c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f736f757263655f64656c74615f706167655f6170706c696564222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22706167655f696e646578223a302c2261636b6e6f776c656467656d656e745f706167655f696e646578223a302c2261636b6e6f776c656467656d656e745f7465726d696e616c223a747275652c226368616e6765645f736f7572636573223a312c2272656d6f7665645f736f7572636573223a312c227265636f6e63696c655f736f7572636573223a5b7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d7d2c7b226d6174657269616c697a655f696e646578223a312c2264656c7461223a7b226b696e64223a2272656d6f766564222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c322c325d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b32382c3136372c3132342c33352c3230332c3230322c3134312c3234322c32302c31332c3134332c3132322c32342c33362c3135312c3137392c34352c34372c3134332c38382c3133302c3232392c3132392c31352c3135372c38332c3130352c332c3235312c3133352c3135362c3137345d2c22736f757263655f646967657374223a5b32382c3136372c3132342c33352c3230332c3230322c3134312c3234322c32302c31332c3134332c3132322c32342c33362c3135312c3137392c34352c34372c3134332c38382c3133302c3232392c3132392c31352c3135372c38332c3130352c332c3235312c3133352c3135362c3137345d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2231636137376332332d636263612d386466322d393430642d386637613138323439376233227d7d7d7d7d5d2c227265706c61796564223a66616c73657d7d7d", "error": "43545850524f0003000000c67b2273657175656e6365223a31322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226572726f72222c22626f6479223a7b22636c617373223a2270726f746f636f6c5f6d69736d61746368222c226d657373616765223a22676f6c64656e2070726f746f636f6c206572726f72222c22726574727961626c65223a66616c73652c2264657461696c73223a6e756c6c7d7d7d", "graph_key_deleted": "43545850524f0003000000817b2273657175656e6365223a332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c65746564222c22626f6479223a7b2264656c65746564223a747275657d7d7d", "graph_key_deletion_prepared": "43545850524f0003000000e97b2273657175656e6365223a322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2267726170685f6b65795f64656c6574696f6e5f7072657061726564222c22626f6479223a7b226368616c6c656e67655f62617365363475726c223a2242675947426759474267594742675947426759474267594742675947426759474267594742675947426759222c22657870697265735f61745f756e6978223a3230302c226b65795f70726573656e74223a747275657d7d7d", "hello": "43545850524f0003000001bd7b2273657175656e6365223a302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2268656c6c6f222c22626f6479223a7b2270726f746f636f6c5f76657273696f6e223a332c2270726f746f636f6c5f66696e6765727072696e74223a2230616233633434356262356237643433336665353635316131643364396131306566373734643537306231333266353065656231343566333033343163376134222c2268656c7065725f76657273696f6e223a22676f6c64656e2d68656c706572222c226361706162696c6974696573223a5b22656e7469746c656d656e745f617574686f72697a6174696f6e222c2267726170685f6b65795f64656c6574696f6e222c22737461747573222c22636f72655f6d6174657269616c697a6174696f6e222c227175657279222c226769745f72656164225d2c22617574686f72697a6174696f6e5f6368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151227d7d7d", - "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a342c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a382c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" + "status": "43545850524f0003000005bc7b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b2263757272656e746e657373223a2263757272656e74222c227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f72656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c22736f757263655f736e617073686f745f736861323536223a2261636661366334326262396637643739633331633932623664306266373030386461393536303139333439623032633130363261376234373631626664346139222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c22636f766572616765223a22636f6d706c657465222c227265706f7369746f72795f636f766572616765223a7b227265706f7369746f72795f63616e6469646174655f6576656e7473223a312c226c6f676963616c5f62696e64696e675f6576656e7473223a312c226365727469666965645f6c6976655f726f6f745f6163636573735f6576656e7473223a312c2266696c655f65766964656e63655f6576656e7473223a312c2265786163745f636f6d6d69745f65766964656e63655f6576656e7473223a312c2265786163745f70756c6c5f726571756573745f65766964656e63655f6576656e7473223a317d2c22636f72655f7072657061726174696f6e5f7065616b5f776f726b657273223a342c22616363657373223a7b22656e7469746c656d656e74223a22617661696c61626c65222c2267726170685f6b6579223a22617661696c61626c65222c226c6f63616c5f7265706f7369746f7279223a22617661696c61626c65227d2c22737570706f727465645f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c22617661696c61626c655f6f7065726174696f6e73223a5b2266696c655f626c616d65222c22636f6d6d69745f626c616d65222c2270756c6c5f726571756573745f626c616d65225d2c2266696e616c697a6174696f6e5f70726f6772657373223a6e756c6c2c2273746f726167655f65766964656e6365223a7b2267726170685f6d616e69666573745f736368656d61223a342c22666c61745f666f726d61745f76657273696f6e223a322c226d6174657269616c697a65725f636865636b706f696e745f76657273696f6e223a382c226a6f75726e616c5f7061636b5f666f726d61745f76657273696f6e223a332c226c65676163795f6a6f75726e616c735f7772697474656e223a302c226a6f75726e616c5f70616765735f7772697474656e223a322c226a6f75726e616c5f7061636b735f7772697474656e223a312c226a6f75726e616c5f66696e6973685f6163746976697479223a7b22776f726b65725f6c696d6974223a312c227065616b5f776f726b657273223a312c22737461727465645f61667465725f7072657061726174696f6e223a747275657d7d7d7d7d" }, "host_frames": { - "apply_core_event_delta_page": "43545850524f0003000011867b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d7d7d7d", - "apply_core_event_delta_pages": "43545850524f00030000118b7b2273657175656e6365223a31322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f7061676573222c22626f6479223a7b227061676573223a5b7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d5d7d7d7d", - "apply_core_source_delta_page": "43545850524f0003000004ab7b2273657175656e6365223a362c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f736f757263655f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d5d7d2c2261636b6e6f776c656467656d656e745f706167655f696e646578223a307d7d7d", + "apply_core_event_delta_page": "43545850524f0003000011867b2273657175656e6365223a382c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d7d7d7d", + "apply_core_event_delta_pages": "43545850524f00030000118b7b2273657175656e6365223a31322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f6576656e745f64656c74615f7061676573222c22626f6479223a7b227061676573223a5b7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a226164646564222c2276616c7565223a7b227265636f72645f76657273696f6e223a322c226576656e745f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a224576656e74222c22646967657374223a5b3231362c39392c3230332c3133322c3130372c3231312c3139322c3131332c3233352c3231392c38332c33382c3139362c37362c3133372c3130362c34342c3133362c34392c31362c3133372c3139392c3232312c3137392c31322c31362c39352c37342c32342c31352c38302c3231305d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2264383633636238342d366264332d383037312d616264622d353332366334346338393661227d2c2273657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c22706172656e745f73657373696f6e5f6964223a6e756c6c2c22726f6f745f73657373696f6e5f6964223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a2253657373696f6e222c22646967657374223a5b3139372c33332c3230362c34372c3137352c3230382c39302c3139312c34352c3135372c3230392c3234342c35332c37392c38312c382c3132322c3235312c3139362c3130392c3231372c34382c3134382c3131302c36312c3133312c3139352c3235342c36312c3132342c34302c38345d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b3232382c3133362c3230322c3138312c32362c35312c39362c35302c342c32312c3135342c3132302c31382c3139342c3131352c39342c32362c36312c3135312c33302c3139302c3230342c31392c3131362c32342c33342c3130332c3130392c36332c37372c3131342c3231325d2c2275756964223a2263353231636532662d616664302d386162662d616439642d643166343335346635313038227d2c2273657373696f6e5f72656c6174696f6e73686970223a22726f6f74222c226576656e745f6f726967696e223a7b226b696e64223a22756e6b6e6f776e227d2c22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c2270726f76696465725f73657373696f6e5f6964223a22676f6c64656e2d73657373696f6e222c226e61746976655f6576656e745f6964223a6e756c6c2c226576656e745f73657175656e6365223a312c226f636375727265645f61745f756e69785f6d73223a313730303030303030303030302c226576656e745f74797065223a226d657373616765222c22726f6c65223a22617373697374616e74222c226167656e745f74797065223a227072696d617279222c2269735f7072696d617279223a747275652c22776f726b7370616365223a6e756c6c2c226272616e6368223a6e756c6c2c22637764223a6e756c6c2c227061727365725f7265766973696f6e223a22676f6c64656e2d7061727365722d7631222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e74223a7b22706f6c6963795f7265766973696f6e223a332c22706f6c6963795f737461747573223a2273656c6563746564222c226e6f726d616c697a65645f626f6479223a22636f6d706c65746520676f6c64656e20436f726520626f6479222c22737472756374757265645f636f6e74656e74223a6e756c6c2c22646973636f766572795f6578636c7573696f6e223a226374785f72657472696576616c5f64657269766564227d2c226d65746164617461223a7b7d2c227265706f7369746f72795f63616e6469646174655f65766964656e6365223a7b227265706f7369746f72795f6f62736572766174696f6e5f7265766973696f6e223a352c22626f756e6465645f7368656c6c5f7375627365745f7265766973696f6e223a342c226173736f63696174696f6e5f706f6c6963795f7265766973696f6e223a362c226f7574636f6d655f636170747572655f7265766973696f6e223a352c2263616e64696461746573223a5b7b226b696e64223a2273657373696f6e5f637764222c2270617468223a222f676f6c64656e2f7265706f227d2c7b226b696e64223a2266696c655f61637469766974795f70617468222c2270617468223a222f676f6c64656e2f7265706f2f7372632f6c69622e7273227d5d7d2c227265706f7369746f72795f62696e64696e6773223a5b5d2c227265706f7369746f72795f61627374656e74696f6e73223a5b5d2c227265706f7369746f72795f66696c655f696e766f636174696f6e5f65766964656e6365223a5b5d2c227265706f7369746f72795f66696c655f6f62736572766174696f6e73223a5b5d2c227265706f7369746f72795f7663735f6f62736572766174696f6e73223a5b5d7d7d5d7d5d7d7d7d", + "apply_core_source_delta_page": "43545850524f0003000004ab7b2273657175656e6365223a362c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a226170706c795f636f72655f736f757263655f64656c74615f70616765222c22626f6479223a7b2270616765223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c22706167655f696e646578223a302c227465726d696e616c223a747275652c2264656c746173223a5b7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d5d7d2c2261636b6e6f776c656467656d656e745f706167655f696e646578223a307d7d7d", "authorize": "43545850524f0003000003f47b2273657175656e6365223a312c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22617574686f72697a65222c22626f6479223a7b22656e7469746c656d656e74223a7b226772616e74223a7b22736368656d615f76657273696f6e223a312c22697373756572223a2268747470733a2f2f636f6d6d65726369616c2e6374782e7273222c226b65795f6964223a22666978747572652d7631222c226772616e745f6964223a226772616e742d31222c227375626a656374223a22757365722d31222c226163636f756e745f6964223a226163636f756e742d31222c2270726f64756374223a226374782d6c6f63616c2d70726f222c226163636573735f6b696e64223a22747269616c222c22696e7374616c6c6174696f6e5f6b65795f7468756d627072696e74223a2241514542415145424151454241514542415145424151454241514542415145424151454241514542415145222c226973737565645f61745f756e6978223a3130302c226e6f745f6265666f72655f756e6978223a39302c22726566726573685f61667465725f756e6978223a3135302c226163636573735f646561646c696e655f756e6978223a3230302c2267726163655f646561646c696e655f756e6978223a3235302c22657870697265735f61745f756e6978223a3137352c226d696e696d756d5f68656c7065725f70726f746f636f6c223a332c227265766f636174696f6e5f65706f6368223a302c226361706162696c6974696573223a5b2267726170685f72656164222c2267726170685f7772697465225d7d2c227369676e61747572655f62617365363475726c223a224167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167227d2c22696e7374616c6c6174696f6e5f7075626c69635f6b65795f62617365363475726c223a2241774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d222c226368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151222c2270726f6f665f7369676e61747572655f62617365363475726c223a224251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251227d7d7d", - "begin_core_materialization": "43545850524f0003000003287b2273657175656e6365223a352c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22626567696e5f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b2268656164223a7b22636f6e74726163745f76657273696f6e223a332c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2267656e65726174696f6e5f6d616e69666573745f76657273696f6e223a342c226964656e746974795f76657273696f6e223a312c22636f72655f7265636f72645f76657273696f6e223a322c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e745f706f6c6963795f7265766973696f6e223a332c227265706f7369746f72795f636f6e74726163745f7265766973696f6e223a31312c226c65786963616c5f736368656d615f76657273696f6e223a332c226c65786963616c5f616e616c797a65725f76657273696f6e223a322c22706f6c6963795f736368656d615f68617368223a2263636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c2265787065637465645f7072696f725f72656365697074223a6e756c6c7d7d7d", + "begin_core_materialization": "43545850524f0003000003287b2273657175656e6365223a352c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22626567696e5f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b2268656164223a7b22636f6e74726163745f76657273696f6e223a332c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2267656e65726174696f6e5f6d616e69666573745f76657273696f6e223a342c226964656e746974795f76657273696f6e223a312c22636f72655f7265636f72645f76657273696f6e223a322c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e745f706f6c6963795f7265766973696f6e223a332c227265706f7369746f72795f636f6e74726163745f7265766973696f6e223a31312c226c65786963616c5f736368656d615f76657273696f6e223a332c226c65786963616c5f616e616c797a65725f76657273696f6e223a322c22706f6c6963795f736368656d615f68617368223a2263636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363222c22736f757263655f736e617073686f745f736861323536223a2261636661366334326262396637643739633331633932623664306266373030386461393536303139333439623032633130363261376234373631626664346139222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c2265787065637465645f7072696f725f72656365697074223a6e756c6c7d7d7d", "blame": "43545850524f00030000018b7b2273657175656e6365223a31312c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22626c616d65222c22626f6479223a7b22746172676574223a7b226b696e64223a22636f6d6d6974222c226f6964223a2233333333333333333333333333333333222c227265706f7369746f7279223a2263747872732f637478227d2c226c696d6974223a31302c22637572736f72223a6e756c6c2c2265787065637465645f736e617073686f74223a7b226b696e64223a22636f7265222c2272656365697074223a7b22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631227d7d7d7d7d", "confirm_graph_key_deletion": "43545850524f0003000004177b2273657175656e6365223a332c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f6e6669726d5f67726170685f6b65795f64656c6574696f6e222c22626f6479223a7b22617574686f72697a6174696f6e223a7b22656e7469746c656d656e74223a7b226772616e74223a7b22736368656d615f76657273696f6e223a312c22697373756572223a2268747470733a2f2f636f6d6d65726369616c2e6374782e7273222c226b65795f6964223a22666978747572652d7631222c226772616e745f6964223a226772616e742d31222c227375626a656374223a22757365722d31222c226163636f756e745f6964223a226163636f756e742d31222c2270726f64756374223a226374782d6c6f63616c2d70726f222c226163636573735f6b696e64223a22747269616c222c22696e7374616c6c6174696f6e5f6b65795f7468756d627072696e74223a2241514542415145424151454241514542415145424151454241514542415145424151454241514542415145222c226973737565645f61745f756e6978223a3130302c226e6f745f6265666f72655f756e6978223a39302c22726566726573685f61667465725f756e6978223a3135302c226163636573735f646561646c696e655f756e6978223a3230302c2267726163655f646561646c696e655f756e6978223a3235302c22657870697265735f61745f756e6978223a3137352c226d696e696d756d5f68656c7065725f70726f746f636f6c223a332c227265766f636174696f6e5f65706f6368223a302c226361706162696c6974696573223a5b2267726170685f72656164222c2267726170685f7772697465225d7d2c227369676e61747572655f62617365363475726c223a224167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167494341674943416749434167227d2c22696e7374616c6c6174696f6e5f7075626c69635f6b65795f62617365363475726c223a2241774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d4441774d222c226368616c6c656e67655f62617365363475726c223a2242415145424151454241514542415145424151454241514542415145424151454241514542415145424151222c2270726f6f665f7369676e61747572655f62617365363475726c223a224251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251554642515546425155464251227d7d7d7d", - "continue_core_materialization": "43545850524f00030000023f7b2273657175656e6365223a31302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f6e74696e75655f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b2265787065637465645f70726f6772657373223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2266696e6973685f726571756573745f646967657374223a2261396532633164626534353963346135646161356634353735353033643036386634333830623837643539633163363530363633653663326339663062623531222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c227068617365223a22656d69745f7265706c6179222c22637572736f725f736861323536223a2264646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464227d7d7d7d", - "core_event_state_page": "43545850524f0003000004bc7b2273657175656e6365223a372c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f73746174655f70616765222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c2261667465725f6576656e745f6964223a6e756c6c2c226d6178696d756d5f6974656d73223a3235367d7d7d", - "finish_core_materialization": "43545850524f0003000003ea7b2273657175656e6365223a392c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2266696e6973685f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2239333031306138653365643731386163616133386332366134316332333064376336396162383362663335643465623831383263306539356539353166646539222c2268656164223a7b22636f6e74726163745f76657273696f6e223a332c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2267656e65726174696f6e5f6d616e69666573745f76657273696f6e223a342c226964656e746974795f76657273696f6e223a312c22636f72655f7265636f72645f76657273696f6e223a322c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e745f706f6c6963795f7265766973696f6e223a332c227265706f7369746f72795f636f6e74726163745f7265766973696f6e223a31312c226c65786963616c5f736368656d615f76657273696f6e223a332c226c65786963616c5f616e616c797a65725f76657273696f6e223a322c22706f6c6963795f736368656d615f68617368223a2263636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363222c22736f757263655f736e617073686f745f736861323536223a2235333137333138653730326565653062366239396635646264646639313938646535313766306135313533306138363339353737623838383339653036373835222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c2265787065637465645f7072696f725f72656365697074223a6e756c6c2c22736f757263655f64656c74615f7061676573223a312c226368616e6765645f736f7572636573223a312c2272656d6f7665645f736f7572636573223a312c226576656e745f64656c74615f7061676573223a322c226576656e745f6d75746174696f6e73223a327d7d7d", + "continue_core_materialization": "43545850524f00030000023f7b2273657175656e6365223a31302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f6e74696e75655f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b2265787065637465645f70726f6772657373223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2266696e6973685f726571756573745f646967657374223a2235383235636233363266353238366332653330646332656666613564653162393364376437386363363433313365366533663434306639663238383636366366222c226d6174657269616c697a65725f7265766973696f6e223a22676f6c64656e2d636f72652d6d6174657269616c697a65722d7631222c227068617365223a22656d69745f7265706c6179222c22637572736f725f736861323536223a2264646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464227d7d7d7d", + "core_event_state_page": "43545850524f0003000004bc7b2273657175656e6365223a372c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22636f72655f6576656e745f73746174655f70616765222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c227265636f6e63696c696174696f6e223a7b226d6174657269616c697a655f696e646578223a302c2264656c7461223a7b226b696e64223a2270726573656e74222c2276616c7565223a7b22736f75726365223a7b2270726f7669646572223a22676f6c64656e222c22736f757263655f666f726d6174223a22676f6c64656e5f6a736f6e6c222c22736368656d615f76617269616e74223a22676f6c64656e2d7631222c2270726f76696465725f6964656e746974795f76657273696f6e223a312c22616e63686f72223a7b22436174616c6f674c696e65616765223a5b312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c312c315d7d2c226964656e74697479223a7b22636f6e74726163745f76657273696f6e223a312c22656e746974795f6b696e64223a22536f75726365222c22646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f646967657374223a5b3137352c3230382c3234342c33362c3138302c3138382c36332c3231382c3132392c3137302c32392c36342c36352c3231362c3131372c3138312c38372c322c3134342c32302c3130352c3131332c3131302c3134382c3130302c3131362c36352c39332c3230322c3232342c3131312c3138375d2c22736f757263655f64657363726970746f725f646967657374223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c2275756964223a2261666430663432342d623462632d386664612d383161612d316434303431643837356235227d7d2c22636f72655f7265636f72645f616363756d756c61746f72223a2261343437653531363466393736616139623932656234386266653335343637313666333463333432373832616333333532646432373636633933633135643139222c226576656e745f636f756e74223a317d7d7d2c22706167655f696e646578223a302c2261667465725f6576656e745f6964223a6e756c6c2c226d6178696d756d5f6974656d73223a3235367d7d7d", + "finish_core_materialization": "43545850524f0003000003ea7b2273657175656e6365223a392c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2266696e6973685f636f72655f6d6174657269616c697a6174696f6e222c22626f6479223a7b226d6174657269616c697a6174696f6e5f6964223a2231373132663833356364353864353462353461656236356631343137326662353331353266366630393764383531623339396133326531363663636230636130222c2268656164223a7b22636f6e74726163745f76657273696f6e223a332c22636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c2267656e65726174696f6e5f6d616e69666573745f76657273696f6e223a342c226964656e746974795f76657273696f6e223a312c22636f72655f7265636f72645f76657273696f6e223a322c22636f72655f7265636f72645f636f6e74726163745f66696e6765727072696e74223a2230363130626539613538313063653734326235303564633463336233646232346539616237393531323661366236326530656632313732653034613835636333222c226e6f726d616c697a6174696f6e5f7265766973696f6e223a312c22636f6e74656e745f706f6c6963795f7265766973696f6e223a332c227265706f7369746f72795f636f6e74726163745f7265766973696f6e223a31312c226c65786963616c5f736368656d615f76657273696f6e223a332c226c65786963616c5f616e616c797a65725f76657273696f6e223a322c22706f6c6963795f736368656d615f68617368223a2263636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363222c22736f757263655f736e617073686f745f736861323536223a2261636661366334326262396637643739633331633932623664306266373030386461393536303139333439623032633130363261376234373631626664346139222c22736f757263655f636f756e74223a312c226576656e745f636f756e74223a317d2c2265787065637465645f7072696f725f72656365697074223a6e756c6c2c22736f757263655f64656c74615f7061676573223a312c226368616e6765645f736f7572636573223a312c2272656d6f7665645f736f7572636573223a312c226576656e745f64656c74615f7061676573223a322c226576656e745f6d75746174696f6e73223a327d7d7d", "hello": "43545850524f0003000001677b2273657175656e6365223a302c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a2268656c6c6f222c22626f6479223a7b2270726f746f636f6c5f76657273696f6e223a332c2270726f746f636f6c5f66696e6765727072696e74223a2230616233633434356262356237643433336665353635316131643364396131306566373734643537306231333266353065656231343566333033343163376134222c22686f73745f76657273696f6e223a22676f6c64656e2d686f7374222c226361706162696c6974696573223a5b22656e7469746c656d656e745f617574686f72697a6174696f6e222c2267726170685f6b65795f64656c6574696f6e222c22737461747573222c22636f72655f6d6174657269616c697a6174696f6e222c227175657279222c226769745f72656164225d7d7d7d", "prepare_graph_key_deletion": "43545850524f0003000000c77b2273657175656e6365223a322c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22707265706172655f67726170685f6b65795f64656c6574696f6e222c22626f6479223a7b22696e7374616c6c6174696f6e5f6b65795f7468756d627072696e74223a2241514542415145424151454241514542415145424151454241514542415145424151454241514542415145227d7d7d", "status": "43545850524f0003000000c97b2273657175656e6365223a342c22726571756573745f6964223a2230303030303030302d303030302d303030302d303030302d303030303030303030303031222c226d657373616765223a7b226b696e64223a22737461747573222c22626f6479223a7b227265717565737465645f636f72655f67656e65726174696f6e5f6964223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161227d7d7d"