Repository navigation
docs(adr): ADR-002 rows-first delivery with server-pushed summary stats - #1043
Merged
Merged
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Contributor
📦 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.dev37519133609or 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.dev37519133609MCP server for Claude Codeclaude mcp add buckaroo-table -- uvx --from "buckaroo[mcp]==0.15.9.dev37519133609" --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 |
This was referenced Oct 6, 2026
Closed
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The client-pull design (stats_request after first paint, resumable units, per-connection cursors, a per-request time budget) is replaced by a server push. The server sends a connection its stats only after the write future of that connection's first row reply resolves, and pushes the tier the ADR-003 stats policy runs automatically. The ADR now assumes ADR-001's cache, so the unit and time-budget decisions (old D5 and D6) are dropped and cold stats move to "Not decided here". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ability (rows-first c2) (#1025) * test(client): failing tests for client hardening under a two-message protocol (rows-first c0a) Tests for the client half of plan 1 phase 0a. A server that sends the summary stats separately, or not at all, leaves the first message without values the client has been assuming. BuckarooView: a change that reached the model before the effect subscribed, one initial_state with metadata decoding once, and out-of-order decodes applying the newer. Pinned rows: a valueless key shows a placeholder with its own row id while df_meta.stats.status is pending, and is omitted when not_computed. The simple tooltip returns nothing for a valueless cell. color_map is silent without bins and restyles when they arrive. An unrelated df_data_dict update keeps the in-flight indicator when the server reports df_meta.stats. One Storybook Playwright test covers pending, not_computed and complete in a real browser, and is added to the Storybook list in scripts/test_playwright_storybook.sh. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(client): harden the client for a two-message protocol (rows-first c0a) BuckarooView reads model.get() for every key once its listeners are in place, so a change:* emitted before the effect ran reaches React. All df_data_dict values go through one decoder (makeLatestDictDecoder) that skips a dict it has already seen and applies only the newest decode, so an initial_state with metadata decodes once and out-of-order decodes cannot overwrite a newer one. standalone.tsx gets the same decoder and catch-up. df_meta.stats.status reaches the grid as stats_status. While pending, a required pinned key with no value becomes a placeholder row that carries its key as index (unique row id, empty value cells). When not_computed it is omitted. A missing df_meta.stats, or any other status, keeps the old behaviour. The simple tooltip returns nothing for a cell with no value or no row data. color_map no longer logs when bins are missing, and the grid refreshes the color-mapped columns when their bins change after the first render. That needs RenderApiModule, which was not registered, so api.refreshCells logged AG Grid error 200 and did nothing. BuckarooInfiniteWidget keeps inFlight when only df_data_dict changed and the server reports df_meta.stats; the answer to the dispatched change is a frame with a new df_meta. Without df_meta.stats the rule is unchanged. The Playwright story test no longer hovers a valueless pinned cell: with the old tooltip it still passed, since AG Grid does not call the tooltip for an empty value. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(client): failing tests for the stats_update channel (rows-first c2) Jest tests, driven through a WebSocketModel with a fake socket, for the client half of the stats wire: a stats_update is key-merged into all_stats and dropped when its stats_gen is not the expected one, the expected gen follows df_meta.stats.gen on every applied initial_state, a merge in flight is discarded or redone when a frame replaces the dict, and stats_aborted moves the status. Plus withStatsCapability and BuckarooServerView putting ?caps=stats_update on the WebSocket URL, and a server Playwright test that the standalone page does the same. StatsChannel.ts is a stub (identity withStatsCapability) so the tests fail on assertions rather than on a missing module. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(client): failing tests for stats_update payloads and an empty dict (rows-first c2) Three more cases for the stats_update merge, found untested after the first test commit: a wide parquet_b64 payload as the server sends it (the shared summary_stats fixture, not a json envelope), a model that holds no df_data_dict yet, and a dict with no all_stats key. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(client): failing test for stats_update ordering (rows-first c2) Two stats_update messages for one gen are applied in arrival order even when the first payload decodes more slowly: the final update merges last and the status completes only after both merges. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(client): merge stats_update into all_stats and advertise the capability (rows-first c2) StatsChannel is the client half of the stats wire. WebSocketModel hands it every text frame first. A stats_update for the gen the model's df_meta reports is decoded and key-merged into df_data_dict.all_stats (new row objects, a new dict), updates apply one at a time in arrival order, and a final update sets df_meta.stats to complete at the update's tier. A merge is dropped when the gen moves on while it decodes and redone when a frame replaces the dict. stats_aborted for the expected gen sets error or not_computed. Other message types are ignored as before. withStatsCapability adds caps=stats_update to a WebSocket URL; both wiring copies (BuckarooServerView and the standalone page) use it so the server treats them as capable clients. The guard tests that already pass before the fix (caps already present, no df_meta.stats, ignored aborts, unknown types, infinite_resp pairing) are added here, with a makeModel(null) helper so a model without stats can be built. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(client): Storybook + Playwright for the stats_update channel (rows-first c2) StatsUpdateChannel renders a real WebSocketModel through BuckarooView in buckaroo mode on a fake in-page server: infinite_request gets an infinite_resp JSON frame plus a binary frame, a buckaroo_state_change (the search box) gets an initial_state carrying the next stats gen with pending, schema-only stats. stats_update frames go out only on a button, one pair per gen, so the tests choose their order against the other frames. Each gen's mean row comes in two column chunks, age then score (final), each padding the other column with null as the wide pivot does. stats-update-channel.spec.ts: - the age chunk fills its pinned cell and leaves the gen pending; the final score chunk fills score, keeps age (its null does not overwrite) and the schema dtype row, and completes df_meta.stats. - after a search moves the client to gen 2, both of gen 1's chunks are dropped; gen 2's chunks merge the filtered rows' means. Checked against StatsChannel mutations: ignoring stats frames or skipping the status change fails both tests, removing the gen checks fails the search test, letting a null overwrite fails the chunk test. Added to the Storybook list in test_playwright_storybook.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… 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>
paddymul
force-pushed
the
adr-002-rows-first-stats-delivery
branch
from
October 6, 2026 18:27
c500780 to
f61df3d
Compare
…ed sessions (rows-first s3) (#1024) * test(server): failing tests for the stats wire format and whole-run stats_request (rows-first s3) On a deferred /load_expr session a stats_request {stats_gen, scope} should return a stats_update with the matching stats_gen whose inline wide payload equals the all_stats an inline session sends, a stale stats_gen should get stats_aborted and run no query, and /load_expr and /reload_expr should bump the generation. df_meta.stats should be injected on every frame and survive a dataflow-field change, which returns the session to the schema tier. With a caps client and a legacy client on one session, the legacy client should keep getting complete messages through the websocket broadcast, the /load_expr, /reload_expr, /load and /load_compare pushes and the highlight overlay, while the caps client gets a stats-free frame and then pulls a stats_update. A spy telemetry sink should see a stats.request span. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): failing tests for stats reuse, component_config and generation edge cases (rows-first s3) Four more cases for the stats wire format, kept in their own commit so each is seen failing on CI before the implementation lands. Returning to a state whose stats were completed once is answered from summary_stats_cache with no query. Completing the stats keeps the session's component_config on the refreshed display config. A warm /load_expr, which rebuilds nothing, leaves stats_gen alone. A stats_request on a session with no data is answered with stats_aborted. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(server): stats_request, stats_update and df_meta.stats on deferred sessions (rows-first s3) A client that advertises ?caps=stats_update gets a stats-free initial_state on a deferred /load_expr session (df_meta.stats.status "pending") and pulls the stats with stats_request {stats_gen, scope}. The reply is a stats_update carrying the dataflow's all_stats as an inline wide envelope, or stats_aborted when the generation is stale. The request is the whole run: one synchronous call that computes the full stats, writes the full-tier summary_stats_cache entry, assigns summary_sd and refreshes the session snapshot through one helper. stats_gen is a server-owned counter bumped by every load handler and by a state change that touches a dataflow field, which also returns a deferred session to the schema tier. df_meta.stats is injected by build_state_message from the session, since the dataflow rebuilds df_meta wholesale. Every send site goes through build_state_message_for, so a client without the capability still gets complete messages (its missing stats run synchronously first); broadcast_state replaces the five copies of the send loop and sends to capable clients first. The stats_request branch binds the session's telemetry sink and emits a stats.request span. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): failing tests for errs on a stats cache hit, the summary dedupe key, span sinks and a failed state change (rows-first s3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(server): set_stats_tier keeps errs per cache entry and rolls back; stats.complete span; failed state change restores the tier (rows-first s3) - A summary_stats_cache hit no longer reports a clean run: the errs of the run that filled an entry are kept beside it (_summary_errs_cache) and set_stats_tier reads them back. errs is assigned before summary_sd so _populate_sd_cache files them with the sd. - set_stats_tier puts the tier and the summary key back when the switch raises. - complete_stats times the run as stats.complete rather than firstpull.stats_total, which also fired for completions long after the load, and binds the session's telemetry sink only when it has one so an outer sink is not replaced by None. - A buckaroo_state_change that raises puts the dataflow's tier back, so it matches the snapshot the session still describes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): the stats wire tests follow main's send path and the stats.complete span (rows-first s3) The legacy overlay test calls _send_client_state, which replaced _send_highlight_overlay on main, the span test expects stats.complete, and the sink test only rules out firstpull.stats_total (the xorq dataflow emits its own firstpull.summary_stats). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): e2e check that eager stats reach the DOM on /load_expr (rows-first s3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): failing e2e check that deferred stats reach the DOM on /load_expr (rows-first s3) The page renders its rows and the schema-tier dtype row, and the stats rows stay empty: no client sends stats_request after the first frame (the scheduler PR #1027 is closed) and the server pushes nothing (ADR-002 D2 is unimplemented). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(server): failing tests that deferred stats are pushed after the first row reply (rows-first s3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * feat(server): push stats after the first row reply on a deferred session (rows-first s3, ADR-002 D2) A client that advertises ?caps=stats_update and is sent a pending frame is owed that generation's stats. The handler registers a continuation on the write future of the client's first row reply; it sends the stats_update once the rows are on the socket, if the connection is still open and the generation is still current. The stats run once per session, so a later connection's push is answered from the session. The stats.push span records the gap since the rows. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
set_stats_tier unpacked the Dict trait into a literal, which basedpyright types as Any with no mapping; it copies with dict() as _populate_sd_cache does. XorqServerDataflow's scope-chain lookup passed the Optional merged_operations to split_chain_by_scope. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…er state change, display config on the final update and a push keyed to the owed generation (rows-first s3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…iting on a timeout (rows-first s3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nge, display config on the final update, push keyed to the owed generation (rows-first s3) A deferred session whose stats run failed goes back to pending when the next connection opens, so a reload retries it. A state change on a completed session holds trait notifications and assigns the tier last, so the cascade runs once for the new state, and a failed change no longer reruns full stats to roll the tier back; the tier a stats_update reports is the session's. The final stats_update carries df_display_args when the stats changed them, and StatsChannel applies it. infinite_request carries the client's stats_gen, and only the reply to a request for the owed generation starts the push. Drops the unused STATS_STATUSES and moves the component_config merge into apply_component_config for /load_expr and the snapshot refresh. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ull, behind a test-only 1 s delay (rows-first s3) BUCKAROO_TEST_STATS_DELAY_S makes complete_stats wait before the full stats run. The server Playwright config sets it to 1, so the summary view is read while only the schema tier has arrived. With it set to 0 the empty check fails. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rid's column definitions (rows-first s3) A jest test drives a real WebSocketModel and the buckaroo widget with AgGridReact stubbed: a final update's df_display_args moves the column's minWidth from 100 to 135 and does not remount the grid. The server e2e also asserts the update's minWidth is larger than the pending frame's. The grid's column widths are not observable in the browser (columns are not resizable and keep their sized width), so the DOM is not asserted. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The rows-first stack changes how a session delivers summary stats: a stats-free first message, a typed
stats_updatekeyed by a server-ownedstats_gen, and stats that follow the first rows. The decisions behind it are spread across the descriptions of a dozen PRs, several of them now closed, and a set of design notes outside the repo. There is no one document to review the design against.Change
Adds
docs/plans/ADR-002-rows-first-stats-delivery.md, status Proposed. It records eight decisions (typedstats_update, a server push after rows,stats_genkeying, the schema tier as a dataflow level, final assignment, legacy-client behavior, search and state changes, telemetry), the alternatives rejected, the consequences, and what is left to ADR-001, ADR-003 and ascan_parquetbackend design (now ADR-004, #1059).D2 was revised after review. The first draft had the client pull stats with
stats_requestand the server run resumable units for a bounded time per request. The ADR now assumes ADR-001's cache works, and the server pushes stats to a connection once the write future of that connection's first row reply resolves, following the ADR-003 stats policy. The unit and time-budget decisions are gone, and cold stats are listed under "Not decided here".The branch also carries the client half of the wire, #1025, which was merged into it.
StatsChannelkey-mergesstats_updateintoall_statsand drops an update for any otherstats_gen, andWebSocketModel,BuckarooServerViewandpackages/js/standalone.tsxaddcaps=stats_updateto the WebSocket URL. Merging this PR lands that code onmain. It has no effect against the server onmain, which ignores the query string inopen()and never sendsstats_update. The default staysinline.Stack
As of 2026-10-06, under the revised D2:
df_meta.stats,stats_gen,stats_update, complete stats for legacy clients, the final assignment). feat(server): stats_request, stats_update and df_meta.stats on deferred sessions (rows-first s3) #1024 does not push. It sends stats only in reply to astats_request, and no client code sends one, so the D2 push is still to be written. Itsstats_requestnarrows to D2's explicit request for a tier above the pushed one, which needs thetierandforcefields it does not take yet.stats_update, advertise the capability).main: fix(client): harden the client for a two-message protocol (rows-first c0a) #1020 (client hardening for a two-message protocol) and fix(server): sequence token on buckaroo_state_change so a stale initial_state is dropped (#998) #1014 (state_seq, independent ofstats_genper D3).StatRun) and feat(server): let a host enable the xorq stats column-chunk split over HTTP (rows-first s6) #1035 (xorq column-chunk split) are open and are to be closed or cut down rather than rebased; the batch split in feat(server): let a host enable the xorq stats column-chunk split over HTTP (rows-first s6) #1035 stays an option for cold stats. feat(client): scheduler requests stats after first paint, with pending, not computed and error states (rows-first c4) #1027's pending, not-computed and error states still apply, and with feat(client): scheduler requests stats after first paint, with pending, not computed and error states (rows-first c4) #1027 closed they need a new client PR.Merging this PR and deleting its branch retargets the open PRs above to
main. The branch name has no slash because the Checks workflow'spull_requesttrigger usesbranches: "*", which does not match a base branch containing a slash.Companion ADRs: ADR-001 (#1040, merged; the stat cache this ADR assumes), ADR-003 (#1044, which stats a session runs at a given size) and ADR-004 (#1059, the
scan_parquetbackend that rows-first delivery waits on).🤖 Generated with Claude Code