Repository navigation
Conversation
This was referenced Oct 19, 2026
This was referenced Oct 19, 2026
Open
Owner
Author
This was referenced Oct 19, 2026
The owner did not accept the residual held-code loss: "It's ok to lose codes that are extremely narrow, but if there was a transition there we should make sure it shows up when zoomed way out." The plot serve dropped a held code 1.49 columns wide on a level-1 read, and on a level-3 read a run 2.2 columns wide (one served column is one canvas pixel). Two causes, both in the serve, neither in the pyramid fold: - decimate_min_max bucketed by point index. A pyramid level folds a flat bucket to one point and a busy one to two, so a bucket of points can be a fraction of a pixel in one place and several pixels in another, and a held run arriving as one point mid-bucket was neither first, last, min nor max. It now bins into max_points equal time columns over the window (clamped to the series' own span, so a fit-all serve over f64::MIN..MAX still works). It keeps each column's first, last, min and max, and the 4 * max_points bound is unchanged. - A level's in-window count only bounds its average spacing. Beside a burst of changes a coarse level's gaps run to several columns, and a run with no point of the slice inside it lies inside one gap. SignalCache::refine_gaps splices finer-level points into every gap wider than a column, one level at a time, and stops as soon as no gap is wider. Every pyramid point is a real sample, so a run held for a column can no longer hide. The serve now guarantees, in served columns: a run held for at least one column is served with its code at every alignment and at every level read. A narrower run may lose its code but not its change: the serve changes value in that column or the one beside it. The one column of placement is inherent to reading the pyramid, since a kept extreme carries its own timestamp from anywhere in its bucket. Placing it exactly needs a raw read at every column edge, which at a wide zoom is the whole capture. On the frontend, drawEnumTiles draws a served run narrower than a device pixel as a one-CSS-pixel tick in its accent, the full band tall, on the lanes axis and the single-enum ribbon, because a sub-pixel fill antialiases to nothing. Measured on the real SignalCache (100 Hz cycling 0..=5, 30 130 samples). The fold and the pyramid (10 034 points above level 0) are unchanged: | | largest held run lost | control serve | points read | |---|---|---|---| | before, 2248 pts (level 1) | 1.49 col | 5651 | 7536 | | after, 2248 pts | 0.74 col | 5986 | 7536 | | before, 280 pts (level 3) | >= 2.20 col | 492 | 492 | | after, 280 pts | 0.98 col | 840 | 1896 | Rejected after measuring: - Reading a 4x finer level gets the change into exactly its own column in every sample tried, but costs +46 % serve and 4x the read. - A per-point transition count in the fold costs 16 B per pyramid point plus a persistence change, still cannot place a change more precisely than its bucket, and does nothing for the held code. ADR 0026 records the guarantee and the measurements. Tests: - a_held_code_one_served_column_wide_survives_the_serve_at_every_alignment (two depths; red before); - every_raw_change_shows_as_a_served_change_within_a_column (random holds and blips; it already held, now pinned); - the sampler's column-edge sweep and a sparse-point case; - the sub-pixel tick on ribbon and lane at ratio 1 and 2. The perf bench's naive-path call to decimate_min_max takes the new whole-range arguments. zoom_in_reads_a_finer_level_than_fit_data asserted 2 * max_points + 2. That bound has been stale since first/last joined the reduction (it passed by luck of its sawtooth), so it now asserts the documented 4 * max_points. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thebhef
force-pushed
the
fix-enum-transitions-zoomed-out
branch
from
October 26, 2026 22:42
64af20f to
eb5d8a4
Compare
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.

The owner did not accept the residual held-code loss: "It's ok to lose
codes that are extremely narrow, but if there was a transition there we
should make sure it shows up when zoomed way out." The plot serve
dropped a held code 1.49 columns wide on a level-1 read, and on a
level-3 read a run 2.2 columns wide (one served column is one canvas
pixel). Two causes, both in the serve, neither in the pyramid fold:
flat bucket to one point and a busy one to two, so a bucket of points
can be a fraction of a pixel in one place and several pixels in
another, and a held run arriving as one point mid-bucket was neither
first, last, min nor max. It now bins into max_points equal time
columns over the window (clamped to the series' own span, so a
fit-all serve over f64::MIN..MAX still works). It keeps each column's
first, last, min and max, and the 4 * max_points bound is unchanged.
burst of changes a coarse level's gaps run to several columns, and a
run with no point of the slice inside it lies inside one gap.
SignalCache::refine_gaps splices finer-level points into every gap
wider than a column, one level at a time, and stops as soon as no gap
is wider. Every pyramid point is a real sample, so a run held for a
column can no longer hide.
The serve now guarantees, in served columns: a run held for at least
one column is served with its code at every alignment and at every
level read. A narrower run may lose its code but not its change: the
serve changes value in that column or the one beside it. The one column
of placement is inherent to reading the pyramid, since a kept extreme
carries its own timestamp from anywhere in its bucket. Placing it
exactly needs a raw read at every column edge, which at a wide zoom is
the whole capture. On the frontend, drawEnumTiles draws a served run
narrower than a device pixel as a one-CSS-pixel tick in its accent,
the full band tall, on the lanes axis and the single-enum ribbon,
because a sub-pixel fill antialiases to nothing.
Measured on the real SignalCache (100 Hz cycling 0..=5, 30 130
samples). The fold and the pyramid (10 034 points above level 0) are
unchanged:
Rejected after measuring:
in every sample tried, but costs +46 % serve and 4x the read.
point plus a persistence change, still cannot place a change more
precisely than its bucket, and does nothing for the held code.
ADR 0026 records the guarantee and the measurements. Tests:
(two depths; red before);
holds and blips; it already held, now pinned);
The perf bench's naive-path call to decimate_min_max takes the new
whole-range arguments.
zoom_in_reads_a_finer_level_than_fit_data asserted 2 * max_points + 2.
That bound has been stale since first/last joined the reduction (it
passed by luck of its sawtooth), so it now asserts the documented
4 * max_points.
Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com