pb30: gz puts the cursor on the collapsed group through landing and
place_cursor, and their column arms were untested. Three rivals passed
all 1948 tests: the arm swapped, landing returning None for columns, and
landing reading col_tuple(0). A new test uses two column levels
(Region over Month) with the cursor at (1, 7), South/May. It requires
the cursor to land on South's Q2 at (1, 5). Each rival fails it and
nothing else. The col_tuple(0) rival needs the two column levels, since
with one level the prefix is empty everywhere.
0r01 (Codex's P2 on #263): innermost_group built open_runs for the
whole axis, cloning a level stack per row, to answer for one row on
every z. open_runs is now a lazy iterator, still the one reader, and
the lookup takes .nth(idx). a_lookup_does_not_read_past_its_row gives
it an unreadable header after row 0 (level usize::MAX). The eager rival
panics with capacity overflow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
uv9e: ToggleGroupAtCursor { is_row: false } (gz) had no command-level
test, and a version reading col_group_for(0) passed the workspace. A new
test seats the cursor at (0, 2) with Month on the columns and requires
Q2. Reading column 0, or reading the row half of the cursor, both fail
it. The row half was already closed: the grid tests sit on row 2, and
injecting row_group_for(0) fails five of them. The browser's z is still
Upstream with no resolver arm (improvise-uln5), so it has no cursor
reading to test.
6u6s: the e265 enumeration could not see run identity. A pruner keying
runs by (level, category, group name) instead of header position
passed. The enumeration now:
- compares every level's run name and whether each survivor shares a
run with the previous one;
- adds three fixtures: a non-contiguous group, a level with no groups,
and two adjacent outer groups;
- builds each layout once per collapse subset and drives
prune_axis_items with a keep predicate per row set: 4,992 cases.
The name-keyed rival now fails at fixture 2, mask 0b101. open_runs is
the one reader of the markers. improvise-core's lib went from
0.86-0.92s to 0.17-0.23s on an Apple M4 Max under load average 39-77.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
On an axis with two grouped categories, runs nest: North in Upper holds
Jan in Q1, and after Q1 ends North/Mar is still in Upper. The markers
carried no level, and every reader was flat, so the nearest marker
decided. Pruning an inner header dropped its GroupEnd too, and a
surviving row changed group. The unpruned answer was lossy as well:
North/Mar was in no group while it was drawn under Upper.
The user ruled per-level membership, with z toggling the innermost group
(the one nearest the cursor). So:
- AxisEntry::GroupHeader and GroupEnd carry a level. expand_category
reads it off its prefix, which holds one item per outer category.
Membership is one open run per level, and a row's group is the
innermost one (row_group_for/col_group_for, hence z).
- prune_axis_items re-marks the survivors instead of filtering the
markers. Before each kept item it emits the markers that make the
output's open runs equal the input's, closing deeper runs innermost-out
first. So each survivor keeps exactly its runs, no header is kept over
nothing, no trailing GroupEnd is left, and when nothing is pruned the
output equals the input. This replaces the pending/close_runs_of/
run_open machinery.
- The wire: FrameGroup gains level (an index into AxisFrame::cats), and
FrameEntry::GroupEnd becomes GroupEnd { level }. groups()/group_of
read the same per-level rule, and group_runs is unchanged (h0iv).
- axis_frame carries the open run at each level above a scrolled
window, not the single nearest header, so a window opening inside
North/Q1 still knows Upper.
Tests:
- an enumeration over every funded mask x collapse subset of a two- and
a three-level fixture (about 4,000 cases), asserting that pruning
moves no row's group, that no header is drawn over nothing and no axis
ends on a marker, and that nothing-pruned is marker-for-marker equal;
- the ruling at the layout and through z;
- frame/layout agreement at every window offset;
- the nested window carry.
Rivals, each injected and restored, are in the evidence files: flat
membership, a pruner that owes no GroupEnd, ends passed through eagerly,
headers kept eagerly, nearest-only window carry, and a flat frame.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
tt22 removed the formula crate's serde dependency with the legacy JSON loader,
keeping serde only where a type crosses the wire. FormulaId crosses the wire, so
it is the third such type. No derive goes back on the formula AST.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
socket.rs: EditTarget takes the union (document_generation, cell, version,
opened); typed_target_frame keeps #205's untouched early return, then claims
as read at the captured generation. Evidence appended after gh/main's.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The reviewer's probe_fold_hidden_measure, over its three rows (hidden,
bogus, no measure). The b114 group rule refuses all three; removing it
fails this test and the b114 test and nothing else.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
App::group_refusal asks the document, not the layout, whether any
coordinate names a group rather than an item, and runs first in
unwritable: a measure-half fold hides from both the stored arm and the
projection. Rival (rule removed) fails the new test and nothing else.
command.md and the ciw8 fold evidence now say the aggregate arm guards
only the None-axis case.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze