Repository navigation
Conversation
📦 TestPyPI package publishedpip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.15.9.dev37502725670or with uv: uv pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.15.9.dev37502725670MCP server for Claude Codeclaude mcp add buckaroo-table -- uvx --from "buckaroo[mcp]==0.15.9.dev37502725670" --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo-table📖 Docs preview🎨 Storybook preview |
|
Review of this PR found one issue, a medium. I reproduced it before changing anything and addressed it. Finding: the schema tier ignored How it was addressed:
The PR body now describes the skipped-column behaviour. It also records one remaining difference: for a skipped column the full tier pre-fills Declined: nothing. CI on 10d9d01: all checks completed successfully, including every |
…the _handle_widget_change split (rows-first s1) A default-tier XorqBuckarooWidget and BuckarooWidget publish df_data_dict, then df_display_args, then the rest of the widget_args_tuple observers on a search change, and merged_sd carries the full stat set. These pass on main and pin the behaviour the split must keep. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-handler fields (rows-first s1) A schema-tier XorqServerDataflow should match full stats on pinned_rows, data_key, summary_stats_key and (except the stats-derived minWidth) column_config, issue no data query besides the cached count, and keep init_sd hints and sorted windows working. A pending state must write nothing under a full-tier cache key, and a later full assignment must reach merged_sd for the raw, clean and filt scopes. assemble_merged_sd must equal merged_sd, and _handle_widget_change must be built from separately callable all_stats and display-args builders. /load_expr and /reload_expr accept stats_tier and stats_delivery, replay them on reload, and keep them out of the warm short-circuit's has_config tuple. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… s1)
Add a dataflow-level stats_tier ("full" default, "schema"). The xorq schema
tier builds identity and typing for every column from the expression's
schema, with no data query beyond the cached row count, so a dataflow
constructs in milliseconds rather than the stats' hundreds.
The tier is part of _scope_cache_key, so a schema entry is never read as a
full one, and _populate_sd_cache stores summary_sd under the filt key only
if it was computed for the current frame, klass list and tier. add_analysis
no longer builds DFStatsClass outside the hook when the tier is not full.
The merged_sd observer body is extracted as the pure assemble_merged_sd,
and _handle_widget_change is split into _build_df_data_dict and
_build_df_display_args.
/load_expr and /reload_expr accept stats_tier and stats_delivery, stored on
the session beside dataflow_kwargs and replayed on reload. They stay out of
the has_config tuple; the warm short-circuit compares the stored pair.
stats_delivery="deferred" builds the schema-tier dataflow and publishes it.
Defaults (full, inline) leave behaviour unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ation test (rows-first s1) The Max Versions jobs resolve pandas 3, which reports a string column's dtype as 'str' where pandas 2 says 'object'. The characterization test asserted 'object'. Verified in a Max Versions environment (pandas 3.0.6, polars 1.44.2, xorq 0.4.5): the unit suite passes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…g on a skipped column (rows-first s1) A column in skip_stat_columns gets only name, dtype and length from the full-tier pipeline, so its _type comes from init_sd. The schema tier layers the schema-derived _type and is_* keys over it, so an int64 column that init_sd types as float merges as integer and renders with zero fraction digits instead of the float displayer init_sd asked for. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ows-first s1) _get_schema_sd never read skip_stat_columns, so a skipped column's schema-derived _type and is_* keys overrode init_sd's _type once merged. The full tier gives a skipped column only name, dtype and length. The schema tier now does the same, so init_sd's _type decides the displayer at both tiers. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lidation (rows-first s1) At the schema tier, add_analysis assigns the new klass list before verify_analysis_objects runs, so a klass that fails validation stays installed and every later add_analysis raises. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssigning it (rows-first s1) A klass that fails verify_analysis_objects no longer stays in analysis_klasses, so later add_analysis calls and a switch to the full tier keep working. Both tiers now validate the candidate list and only then assign it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…F-8 (rows-first s1) _optional_body catches JSONDecodeError and TypeError, but json.loads on bytes that aren't valid UTF-8 raises UnicodeDecodeError, so the reload returns 500 where a body it can't parse should be ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… s1) _optional_body catches ValueError, which covers both JSONDecodeError and the UnicodeDecodeError json.loads raises for non-UTF-8 bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1e040e4 to
a03bfa0
Compare
…r, add_analysis and /load (rows-first s1) - Assigning stats_tier changes nothing: it is a plain attribute, so a schema dataflow switched to full keeps its schema stats, and any string is accepted. - set_stats_tier, which installs a summary computed elsewhere (or one an earlier visit cached) without computing the filtered scope again, does not exist. The two tests that assigned summary_sd after flipping the tier now go through it or the trait. - add_analysis never reruns the summary stats, so an added stat never reaches merged_sd. - At the schema tier, an op under a search counts the rebuilt clean-scope frame as well as the filtered one. - /load leaves the stats policy on the session, so a later /load_expr that omits it inherits an earlier load's deferred delivery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n hand, add_analysis reruns the summary (rows-first s1) stats_tier is now a traitlets Enum that _summary_sd observes. Assigning it validates the value and computes the new tier's stats, so switching tiers no longer means assigning summary_sd by hand. set_stats_tier switches without that compute when the stats are already in hand: a summary computed elsewhere (stored under the state's key at the new tier), or the entry an earlier visit cached. It records the dedupe key first, so the observer doesn't redo them. _summary_sd records its dedupe key only after the stats are computed. The _populate_sd_cache guard no longer computes the filt scope itself: a summary_sd that isn't this frame's is left out of the cache, and the next run fills it. That removes the synchronous recompute on a tier switch and the errs it dropped. add_analysis builds the klass list with with_stat, the rule both pipelines' add_stat now share, validates it, then reruns _summary_sd. The full-tier branch used to run two stat pipelines and throw both away, so an added stat never reached merged_sd. The tier is checked once, in CustomizableDataflow._get_summary_sd, which dispatches to _get_full_sd or _get_schema_sd. XorqDataflow overrides _get_full_sd and keeps only its pandas-frame branch in _get_summary_sd. Its _get_schema_sd takes the scope and gives the clean scope no length: that frame is rebuilt on every cache miss, so its count was a query of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s are defined once (rows-first s1) /load clears the xorq state a /load_expr left but kept stats_tier and stats_delivery, which /load_expr reads as the defaults for a re-POST that omits them. A /load in between therefore handed an earlier load's deferred delivery to the next /load_expr. /load now resets the pair. The new-session defaults were hard-coded in LoadExprHandler as well as on SessionState. Both now read DEFAULT_STATS_TIER and DEFAULT_STATS_DELIVERY from session.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t imports move to module level (rows-first s1) BuckarooInfiniteWidget._handle_widget_change repeated the df_data_dict and df_display_args assembly this PR moved into _build_df_data_dict and _build_df_display_args. It now calls them. Its dataflow is built with skip_main_serial, so 'main' stays empty as before. assemble_merged_sd, Expr and XorqStatPipeline were imported inside test functions, against the repo's rule. They are now module-level imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A second review of this PR (at 10d9d01) found 14 issues. I rebased the branch onto main after #1040 merged, so the earlier fixes have new SHAs (old ones in parentheses), and the rest of the work sits on top of that. Fixed
Changed in the process. At the full tier, a klass that fails DAG validation now makes Corrected: 3. The finding was that one failed stats run leaves wrong filtered stats. Tracing again turned up a separate bug, already present on main. When Declined: nothing. For #1024 and #1035: Local: 1642 passed, 17 skipped, 16 xfailed; ruff and paddy-format pass. CI on c61b221: all 24 Checks jobs passed, including every |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
… s1) (#1021) * test(dataflow): characterize widget trait order and merged_sd before the _handle_widget_change split (rows-first s1) A default-tier XorqBuckarooWidget and BuckarooWidget publish df_data_dict, then df_display_args, then the rest of the widget_args_tuple observers on a search change, and merged_sd carries the full stat set. These pass on main and pin the behaviour the split must keep. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(xorq): failing tests for the xorq schema stats tier and its load-handler fields (rows-first s1) A schema-tier XorqServerDataflow should match full stats on pinned_rows, data_key, summary_stats_key and (except the stats-derived minWidth) column_config, issue no data query besides the cached count, and keep init_sd hints and sorted windows working. A pending state must write nothing under a full-tier cache key, and a later full assignment must reach merged_sd for the raw, clean and filt scopes. assemble_merged_sd must equal merged_sd, and _handle_widget_change must be built from separately callable all_stats and display-args builders. /load_expr and /reload_expr accept stats_tier and stats_delivery, replay them on reload, and keep them out of the warm short-circuit's has_config tuple. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(xorq): stats tier machinery and the xorq schema tier (rows-first s1) Add a dataflow-level stats_tier ("full" default, "schema"). The xorq schema tier builds identity and typing for every column from the expression's schema, with no data query beyond the cached row count, so a dataflow constructs in milliseconds rather than the stats' hundreds. The tier is part of _scope_cache_key, so a schema entry is never read as a full one, and _populate_sd_cache stores summary_sd under the filt key only if it was computed for the current frame, klass list and tier. add_analysis no longer builds DFStatsClass outside the hook when the tier is not full. The merged_sd observer body is extracted as the pure assemble_merged_sd, and _handle_widget_change is split into _build_df_data_dict and _build_df_display_args. /load_expr and /reload_expr accept stats_tier and stats_delivery, stored on the session beside dataflow_kwargs and replayed on reload. They stay out of the has_config tuple; the warm short-circuit compares the stored pair. stats_delivery="deferred" builds the schema-tier dataflow and publishes it. Defaults (full, inline) leave behaviour unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(dataflow): accept pandas 3's str dtype in the pandas characterization test (rows-first s1) The Max Versions jobs resolve pandas 3, which reports a string column's dtype as 'str' where pandas 2 says 'object'. The characterization test asserted 'object'. Verified in a Max Versions environment (pandas 3.0.6, polars 1.44.2, xorq 0.4.5): the unit suite passes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(xorq): failing test for the schema tier overriding init_sd typing on a skipped column (rows-first s1) A column in skip_stat_columns gets only name, dtype and length from the full-tier pipeline, so its _type comes from init_sd. The schema tier layers the schema-derived _type and is_* keys over it, so an int64 column that init_sd types as float merges as integer and renders with zero fraction digits instead of the float displayer init_sd asked for. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(xorq): schema tier leaves a skipped column's typing to init_sd (rows-first s1) _get_schema_sd never read skip_stat_columns, so a skipped column's schema-derived _type and is_* keys overrode init_sd's _type once merged. The full tier gives a skipped column only name, dtype and length. The schema tier now does the same, so init_sd's _type decides the displayer at both tiers. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(xorq): failing test for a schema-tier add_analysis that fails validation (rows-first s1) At the schema tier, add_analysis assigns the new klass list before verify_analysis_objects runs, so a klass that fails validation stays installed and every later add_analysis raises. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(xorq): schema-tier add_analysis validates the klass list before assigning it (rows-first s1) A klass that fails verify_analysis_objects no longer stays in analysis_klasses, so later add_analysis calls and a switch to the full tier keep working. Both tiers now validate the candidate list and only then assign it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(server): failing test for /reload_expr with a body that isn't UTF-8 (rows-first s1) _optional_body catches JSONDecodeError and TypeError, but json.loads on bytes that aren't valid UTF-8 raises UnicodeDecodeError, so the reload returns 500 where a body it can't parse should be ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(server): /reload_expr ignores a body that isn't UTF-8 (rows-first s1) _optional_body catches ValueError, which covers both JSONDecodeError and the UnicodeDecodeError json.loads raises for non-UTF-8 bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(dataflow): failing tests for the stats_tier trait, set_stats_tier, add_analysis and /load (rows-first s1) - Assigning stats_tier changes nothing: it is a plain attribute, so a schema dataflow switched to full keeps its schema stats, and any string is accepted. - set_stats_tier, which installs a summary computed elsewhere (or one an earlier visit cached) without computing the filtered scope again, does not exist. The two tests that assigned summary_sd after flipping the tier now go through it or the trait. - add_analysis never reruns the summary stats, so an added stat never reaches merged_sd. - At the schema tier, an op under a search counts the rebuilt clean-scope frame as well as the filtered one. - /load leaves the stats policy on the session, so a later /load_expr that omits it inherits an earlier load's deferred delivery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(dataflow): stats_tier is a trait, set_stats_tier installs stats in hand, add_analysis reruns the summary (rows-first s1) stats_tier is now a traitlets Enum that _summary_sd observes. Assigning it validates the value and computes the new tier's stats, so switching tiers no longer means assigning summary_sd by hand. set_stats_tier switches without that compute when the stats are already in hand: a summary computed elsewhere (stored under the state's key at the new tier), or the entry an earlier visit cached. It records the dedupe key first, so the observer doesn't redo them. _summary_sd records its dedupe key only after the stats are computed. The _populate_sd_cache guard no longer computes the filt scope itself: a summary_sd that isn't this frame's is left out of the cache, and the next run fills it. That removes the synchronous recompute on a tier switch and the errs it dropped. add_analysis builds the klass list with with_stat, the rule both pipelines' add_stat now share, validates it, then reruns _summary_sd. The full-tier branch used to run two stat pipelines and throw both away, so an added stat never reached merged_sd. The tier is checked once, in CustomizableDataflow._get_summary_sd, which dispatches to _get_full_sd or _get_schema_sd. XorqDataflow overrides _get_full_sd and keeps only its pandas-frame branch in _get_summary_sd. Its _get_schema_sd takes the scope and gives the clean scope no length: that frame is rebuilt on every cache miss, so its count was a query of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(server): /load resets the session's stats policy, and its defaults are defined once (rows-first s1) /load clears the xorq state a /load_expr left but kept stats_tier and stats_delivery, which /load_expr reads as the defaults for a re-POST that omits them. A /load in between therefore handed an earlier load's deferred delivery to the next /load_expr. /load now resets the pair. The new-session defaults were hard-coded in LoadExprHandler as well as on SessionState. Both now read DEFAULT_STATS_TIER and DEFAULT_STATS_DELIVERY from session.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: the infinite widget reuses the dataflow's builders, and test imports move to module level (rows-first s1) BuckarooInfiniteWidget._handle_widget_change repeated the df_data_dict and df_display_args assembly this PR moved into _build_df_data_dict and _build_df_display_args. It now calls them. Its dataflow is built with skip_main_serial, so 'main' stays empty as before. assemble_merged_sd, Expr and XorqStatPipeline were imported inside test functions, against the repo's rule. They are now module-level imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Problem
On
/load_expr,XorqServerDataflowcomputes summary stats inside its constructor. Over 115 tallyman loads stats are a median 93% offirstpull.load_expr(p50 1.29 s, max 215.8 s), andsession.xorq_dataflowis only assigned after construction, so no row is served until they finish. A schema-only construction takes about 1 ms against 393 ms with stats on a 27-column entry, but nothing builds a dataflow at that level today, and three details of the dataflow get in the way:merged_sdandcolumn_configneed more than an empty sd. An emptymerged_sdyields zero columns, andskip_stat_columnsdrops_type, so every column renders asobj._scope_cache_keyignores what the sd contains. After a schema-only construction, assigning full stats tosummary_sdhits the three cached scope entries and never reachesmerged_sd._populate_sd_cachealso stores the currentsummary_sdunder a new filt key without checking it belongs there.add_analysisbuildsDFStatsClassitself (and processes the frame twice), so it bypasses any gate placed in_get_summary_sd.Phase and plan references
Rows-first s1, from
buckaroo2-reports/plans/: plan 2 (02-rows-first-xorq-and-lazy-polars.md) section 7 "Phase 1" and sections 3, 4.1 and 4.2; plan 1 (01-rows-first-stats-separate-plumbing.md) sections 3 (constraints 1 to 4 and 11), 4.0 and 9 "Phase 1", limited to the parts plan 2 Phase 1 owns (the_populate_sd_cacheguard and the_handle_widget_changesplit). Plan 3 is background only.Approach
CustomizableDataflow(..., stats_tier="full" | "schema").stats_tieris a traitletsEnumonDataFlowthat_summary_sdobserves: assigning it refuses an unknown value and computes the new tier's stats.set_stats_tier(tier, summary=None)switches without that compute when the stats are already in hand: a(sd, errs)computed elsewhere, or the entry an earlier visit left insummary_stats_cache.XorqDataflowimplements the schema tier; pandas and polars raiseNotImplementedErrorfor it until the next phase.CustomizableDataflow._get_summary_sd, which calls_get_full_sdor_get_schema_sd(processed_df, scope).XorqDataflowoverrides_get_full_sdand keeps only its pandas-frame branch in_get_summary_sd.orig_col_name,rewritten_col_name,dtype, theis_*flags,_typeandlength. Types come from the sametyping_statsand_typestats the pipeline runs (newschema_stats(dtype)inxorq_stats_v2.py),lengthfrom the cached_expr_count. No stat query is issued. The clean scope's sd has nolength: its frame is rebuilt on every cache miss, so counting it would be a query of its own. The filtered frame's count is the onedf_metatakes anyway. A column inskip_stat_columnsgets onlyorig_col_name,rewritten_col_name,dtypeandlength, as it does at the full tier, so its_typecomes frominit_sdat both tiers._scope_cache_key(new optionaltier=argument, defaulting to the dataflow's) and of the_summary_sddedupe key, which_summary_sdnow records only after the stats are computed._populate_sd_cachestoressummary_sdunder the filt key only if it was computed for the current frame, klass list and tier. Otherwise it leaves the entry out, and the next_summary_sdrun for that state fills it.add_analysisbuilds the klass list withwith_stat, the replace-or-append rule both pipelines'add_statnow share, validates it, then reruns_summary_sd, at either tier. It no longer buildsDFStatsClassitself.merged_sdobserver body is now the pureassemble_merged_sd(init_sd, cleaned_sd, raw_sd, processed_sd, processed_df, chains, clean_sd=None, filt_sd=None)._handle_widget_changecalls_build_df_data_dict(rows,all_stats, placeholder) and then_build_df_display_args(column_config, pinned_rows, overrides), in the order it set the traits before.BuckarooInfiniteWidget._handle_widget_changecalls the same two builders instead of repeating them./load_expracceptsstats_tier(full|schema) andstats_delivery(inline|deferred). They are stored on the session asstats_tierandstats_delivery, besidedataflow_kwargs, and replayed by/reload_expr, which also accepts them in an optional body. The dataflow is built atdataflow_stats_tier(stats_tier, stats_delivery): schema when delivery isdeferred, elsestats_tier. A field the body omits keeps the session's value, ascache_dirdoes./loadresets the pair toDEFAULT_STATS_TIERandDEFAULT_STATS_DELIVERY(session.py), as it clears the rest of the xorq state. Unknown values return 400 (invalid_stats_tier,invalid_stats_delivery).has_config. The warm short-circuit compares the requested pair with the stored one and rebuilds only when they differ.deferredthis phase only builds the schema-tier dataflow and publishes it. Stats never arrive; that is the next phases.What changes
buckaroo/dataflow/dataflow.py:STATS_TIERS,assemble_merged_sd, thestats_tiertrait andset_stats_tier, the tier dispatch in_get_summary_sdwith the_get_full_sdand_get_schema_sdhooks, the tier in_scope_cache_keyand in the_summary_sddedupe key, the_populate_sd_cacheguard,add_analysis,_build_df_data_dictand_build_df_display_args.buckaroo/xorq_buckaroo.py,buckaroo/customizations/xorq_stats_v2.py:XorqDataflow._get_full_sdand_get_schema_sd,schema_stats.buckaroo/pluggable_analysis_framework/stat_pipeline.py,xorq_stat_pipeline.py:with_stat, used by bothadd_stats.buckaroo/buckaroo_widget.py:BuckarooInfiniteWidget._handle_widget_change.buckaroo/server/session.py:SessionState.stats_tierandstats_delivery,STATS_DELIVERIES,DEFAULT_STATS_TIER,DEFAULT_STATS_DELIVERY,dataflow_stats_tier.buckaroo/server/handlers.py:_stats_policy_from_body,LoadHandler,LoadExprHandlerandReloadExprHandler.Tests
Three test and fix commits, in this order (the third is a test-only fix, below):
XorqBuckarooWidgetandBuckarooWidgetkeep the order in which a search change reaches the dataflow's and widget's traits, and theirmerged_sdkeys and values (tests/unit/test_xorq_buckaroo_widget.py,tests/unit/basic_widget_test.py).tests/unit/server/test_load_expr.py,TestStatsTierSchema: a schema-tierXorqServerDataflowmatches full stats onpinned_rows,data_key,summary_stats_keyandcolumn_configwithoutminWidth, andminWidthis the one difference for a float with a 1e9 maximum; its sd is the schema keys of the full sd; a spy onExpr.execute,Expr.to_pyarrowandXorqStatPipeline._executesees onlyCountStarthrough construction, a search andadd_analysis(a control test shows the spy does see stat queries at the full tier);init_sdhints andcolumn_config_overridesapply; a sortedinfinite_requestworks; a schema-tier state with a user op and a search active writes nothing under a full-tier key; switching the tier gives amerged_sdequal to a full-tier dataflow's for raw, clean and filt; full-tier entries written to the cache first are used without a stat query (both reworked in the second follow-up below); an sd from another tier is not cached under the new tier's key.TestLoadExprStatsPolicy: defaults, schema tier,deferredpublishing a schema dataflow that serves rows with no stat query, 400s, a warm re-POST with an unchanged pair short-circuiting (also when the fields are omitted, and when the default pair is sent on every POST) and with a changed pair rebuilding,/reload_exprreplaying and replacing the pair.tests/unit/dataflow/scoped_summary_stats_test.py:assemble_merged_sdequalsmerged_sdwithinit_sdoverrides, cleaning and a search; its startup branch; it does not mutate its inputs.tests/unit/dataflow/customizable_dataflow_test.py: the two builders are callable apart, and building display args serializes no stats.On the failing-tests commit (9b96220) all nine
Python / Testmatrix jobs failed (3.11 to 3.14, Max Versions 3.11 to 3.14, Windows). CI's annotation only says the step exited 1, so the reasons come from running that commit locally: 23 of the new tests fail: the dataflow tests on the missingstats_tierargument, the handler tests on the missing session attributes and validation, the assemble and split tests on the missing functions, and the deferred-load test on the stat queries it still issues. The characterization tests pass.test_the_spy_sees_stat_queries_at_the_full_tierandtest_the_default_pair_sent_on_every_post_does_not_defeat_the_warm_exitpass on main by design: the first is a control for the spy, the second guards thehas_configrequirement, which only a wrong fix can break.The implementation commit (7f56c20) passed every
Python / Testjob except the fourMax Versionsones. Those resolve pandas 3, which reports a string column's dtype asstr, and my pandas characterization test assertedobject. I reproduced it in a Max Versions environment (pandas 3.0.6, polars 1.44.2, xorq 0.4.5), loosened that one assertion in 21cd73e, and the unit suite then passed there (1213 passed). On 21cd73e every check completed successfully, including all ninePython / Testjobs,Python / Lint, the JS job and the six Playwright jobs.Review follow-up: the schema tier first ignored
skip_stat_columns, so for a skipped column its schema-derived_typeandis_*keys overrode the_typeininit_sd(an int64 column thatinit_sdtypes as float merged asintegerand rendered with zero fraction digits).test_skipped_column_keeps_init_sd_typinginTestStatsTierSchemacovers it. It was committed alone (1c89cfd) andPython / Test (3.11)failed on that commit; the fix is 10d9d01. The 3.14 job passes on the test-only commit because xorq is gated to python<3.14 and these tests skip there.Second review follow-up, after the rebase onto main with #1040. The failing tests are in 739f2c1: assigning
stats_tierchanged nothing and accepted any string;set_stats_tierdid not exist (the two tests that assignedsummary_sdafter flipping the tier now go through it or the trait);add_analysisnever reran the summary stats, so an added stat never reachedmerged_sd; at the schema tier an op under a search counted the rebuilt clean-scope frame as well as the filtered one; and/loadleft the stats policy for the next/load_exprto inherit. All seven failed locally. The fixes are 55d70bb (dataflow) and 9609772 (server), and c61b221 has the infinite widget reuse the builders and moves test imports to module level.The rebase rewrote the earlier commits: 9b96220, 7f56c20, 21cd73e, 1c89cfd and 10d9d01 above are now 689aae8, 5db0cfc, f304948, 0ef794e and 91e1ff5. The CI results quoted for them ran before the rebase.
Construction on a 200,000-row, 20-column parquet (10 float and 10 string columns) took about 395 ms at the full tier and 4 ms at the schema tier, measured in-process with
XorqServerDataflow.Why default behaviour is unchanged
stats_tierdefaults tofulleverywhere;dataflow_stats_tier("full", "inline")isfull, which is what/load_exprand/reload_exprsend when the fields are absent.|fullcomponent (they are opaque, in-process strings). The_populate_sd_cacheguard compares(id(processed_df), id(analysis_klasses), tier)with the key_summary_sdrecords after computing. That matches at the full tier unless the last run raised, and then the filt entry is left out rather than filled with the previous frame's sd.assemble_merged_sdis the old observer body moved; the observer passes it the same values._handle_widget_changebuilds the same two values in the same order, which the characterization commit pins./reload_exprstill works with no body or a body it cannot parse (it reads the stats fields leniently).add_analysisnow reruns the summary stats once, so an added stat reachesmerged_sd; before, it ran the pipeline twice and dropped the result. A klass that fails DAG validation now raises (DAGConfigError, orTypeErrorfor a v1 class) and leaves the list as it was, at both tiers and on both backends; before, the full tier printed "Unit tests failed" (pandas) or "DAG validation failed" (xorq) and carried on.Deviations from the plan
_get_summary_sdof bothxorq_buckaroo.pyand the basedataflow.py. The base branch calls a new_get_schema_sdhook that raisesNotImplementedError; pandas and polars implement it in the next phase.stats_tieras dataflow-level, and plan 2 saysdeferred"builds a schema-tier dataflow". I readstats_tieron the session as the tier the session is headed for andstats_delivery="deferred"as forcing the dataflow to be built atschemauntil later phases deliver the rest. Sostats_tier="full"withdeferredis a schema dataflow whose session still targetsfull.summary_stats_cacheentry before assigning full stats. With the tier in the key that is unnecessary: the full-tier entry is a different key. Assigningstats_tiercomputes the new tier's stats, andset_stats_tierinstalls stats computed elsewhere, or full-tier entries already cached, without recomputing the filtered scope (both are tested)._populate_sd_cachechange as a guard against a deferral returning the previous sd. The deferral path here builds a fresh schema sd for each frame, so what the guard covers is asummary_sdleft from an earlier frame because the last run raised. It leaves that entry out rather than computing it. A hook that deliberately returns a stale sd would still be stored; none exists.stats_tieris a trait onDataFlow(the base), since_summary_sdand its dedupe key live there.stats_tierorstats_delivery(or sending null) keeps the session's value rather than resetting to the default. The plan leaves this open; it followscache_dir(server: /load_expr loads builds without a cache_dir — cache nodes resolve to ~/.cache/xorq, so an embedder's baked snapshots are never read #972)./reload_exprtakes an optional body for the pair; the plan only says it replays the stored one.ag_grid_specs.minWidthdiffers between tiers in the default styling, so the comparison drops that one key. Other klasses that read min, max or histogram will differ the same way.min,maxanddistinct_countwithNone, and those overrideinit_sd'sminandmaxinmerged_sd. The schema tier leaves them toinit_sd, so a klass that readsminormaxof a skipped column seesinit_sd's values at the schema tier andNoneat the full tier. The default styling'scolumn_configis the same at both.Not in this PR
Pandas and polars schema tiers;
df_meta.stats,stats_request,stats_update,?caps=andstats_gen; resumable stat units andStatRun; the filtered-count memoization; any default flip. Underdeferred, stats do not arrive yet; the phase that delivers them should install them withset_stats_tier("full", summary).🤖 Generated with Claude Code