Skip to content

fix: stop an arriving code answer from freezing the pane for its length - #631

Merged
breferrari merged 11 commits into
mainfrom
issue-530-note-rows-follow-the-window
Sep 30, 2026
Merged

breferrari merged 11 commits into
mainfrom
issue-530-note-rows-follow-the-window

Conversation

@breferrari

@breferrari breferrari commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

An answer the agent quotes as code no longer freezes the pane for the length of the block when it arrives, and a note taller than the pane costs its count and its drawn rows rather than every row it has. Closes #530. Plan: #530 (comment). Deviations: #530 (comment) and #530 (comment).

Before, answer_of parsed every line of a fenced block on the frame the answer arrived, and Pin::rows built every row of every note twice a frame before the clamp kept a screenful. Now a note is one list of sections (Pin::sections) that the clamp's count sums and the build walks, built only from the first row the clamp keeps to the last the pane holds; a quoted block keeps its parser position in the highlighter and is parsed as far as the last line the frame draws (Pass::quoted_to), and every pinned answer holds its blocks each frame (Pass::hold, no digest) so the sweep keeps them. HighlightStats::quoted_lines counts the lines of blocks parsed and View::built the note rows a screen built.

Scope: full. Label release by path. SPEC.md's I4 paragraph carries the rule, attributed to the reader's plan approval; RULINGS.md carries the numbers and the three rejected shapes.

Test plan

  • budgets.rs::arrival_holds_the_budget, release: a 2,000-line fenced answer arrives on every sample, timed on the 80x24 pane against I9's 16ms and read on the 80x160 pane, asserting on both that the lines parsed are no more than the rows drawn and that every parsed line drew a class. On main the tall pane read 119.98ms p50 fenced against 15.25ms as prose and parsed the whole block, which is the red run; the ordinary pane was not timed on main. On this branch: 80x24 fenced 7.98ms against 7.52ms prose, 8 lines parsed for 13 rows; 80x160, when it was timed, 18.69ms against 13.14ms prose, 110 lines parsed for 115 rows. The tall pane's remainder is its window, a hundred-odd lines of grammar, the first-touch shape §7 leaves outside I9.
  • notes.rs::parse_follows_the_window: the arrival parses no more lines than the pane has rows, a second screen parses none, and a screenful down parses at most another screenful.
  • notes.rs::rows_follow_the_window: a 10,000-line prose answer fills the screen and builds exactly the note rows drawn.
  • notes.rs::kept_while_pinned: a note whose line stays on the pane with its whole answer behind the window for two frames is not parsed again when its rows return.
  • notes.rs::return_costs_a_screenful: a note whose line leaves the pane for two frames is swept, and coming back parses at least one line and at most a screenful, where main parsed the whole block again.
  • view::tests::count_matches_build: every note shape at seven widths counts what its build produces, and every window of it is the same rows.
  • The quoted-answer steady gate holds: 12.56ms fenced against 12.45ms prose on the 80x160 pane.
  • Mutation battery on the final code: killed parse the whole block, build every row of the answer, build every row of the note, ask the whole block at emit, drop the keep.
  • warm.rs::held_demand: a cold block held behind the window for two frames keeps demanding its grammar and is not rebuilt; once warmed, the next hold rebuilds it exactly once, the demand ends and a draw finds colour.
  • Suites, nextest: 714 across the note-related suites plus register and package; core 452; doctests 4; clippy clean under -D warnings. The full workspace runs in CI.

Review

  • /simplify: one description of a note's rows (Section) shared by count and build; the window functions report the rows they walked so no part is counted before it is built; the code window is walked once for its pieces and its end; pins are found by row index; the box rows are windowed by a drain; the tall pane is read rather than timed; the two budget gates share the coloured-rows count; the notes gates use the file's own note builder. Skipped: memoising the count on the pin (two walks per frame now, count and build, over the reader's short body); the quadratic remainder scan in prose_pieces, which predates this change and is named in the ledger; the chunks allocation per frame at placement, which also predates it.
  • /two-axis-review: test names as labels, the row gate holding the counter to the rows drawn, the rule attributed and its history moved to the ledger, the bottom clamp's cost recorded, the section window and the sum with one name each, three docblocks trimmed. Skipped: a struct for note_widths and for wrap_rows's eight arguments, the shape the file's other painters already allow; the quoted_to argument list, which mirrors quoted; keeping Highlighter::quoted, which the coverage tests call.
  • /code-review high: a pinned answer's blocks are held each frame so the sweep does not drop them and an uncompiled grammar is demanded before the block is drawn; one highlighter in the arrival rig; the five-word gate renamed; the quoted counter's doc corrected. The scroll-away case it named is not a regression: a note whose line leaves the pane is not pinned on main either, and its block was re-parsed whole there; here the return costs a screenful, and return_costs_a_screenful holds that. Skipped: caching the Answer per note write (chunks and the digest per frame predate this change; the prose arm's 13.14ms arrival on the tall pane bounds what a held 2,000-line answer costs a frame); folding Answer::rows_in onto section_window (a part is not counted before it is walked); the double body walk in Pin::sections (the body is the reader's short text).
  • /code-review of the fix commit: the hold takes no digest; the keep has its own gate, kept_while_pinned, and a mutation that drops it goes red; the return gate has a lower bound; the counter doc says the line, not the file; the keep's condition is an if. Skipped: none.
  • /code-review of the second fix commit: the hold skipped the thaw a draw applies, so a held block whose grammar was warmed meanwhile kept demanding it; the demand is now one function both paths call, the hold rebuilds a thawed entry, and the warm suite gates both halves. Accepted and said in the code: a rewrite behind the window is seen when the block is next drawn, which is the cost of holding without a digest.
  • /code-review of the third fix commit: the stale test is one function the hold and the draw share, and it also fires when a held block's fence now names another grammar, so a rewrite behind the window cannot pin the old grammar's demand; the warm gate holds the rebuild count; the hold's doc states its cost per case; the gate's name is a label. The fourth commit carries only those asks and their gates and had no further agent pass.
  • Copilot: no review; the reviewer reported its quota limit reached.

Release note

Release-note: An answer the agent quotes as code no longer freezes the pane for the length of the block when it arrives.

🤖 Generated with Claude Code

breferrari and others added 3 commits September 30, 2026 22:19
A note's rows were built whole on every frame, twice, before the clamp kept the screenful that fits, and a fenced block in an answer was parsed whole on the frame it arrived. A note is now counted without building a row, and built from the first row the clamp keeps to the last the pane holds; a quoted block keeps its parser position and is parsed as far as the last line the frame draws.

Frame::diff_with's counterpart here is Pass::quoted_to, which fills a block forward to a line. The answer no longer carries spans at placement: the rows the clamp keeps ask for them. View::built counts the note rows a screen built, and three gates hold the shape: the frame a 2,000-line fenced answer arrives on parses no more lines than the pane has rows and holds I9's budget, a second screen parses nothing more, and a 10,000-line answer builds at most a screenful of rows.

Release-note: An answer the agent quotes as code no longer freezes the pane for the length of the block when it arrives.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
SPEC.md's I4 paragraph says a note's rows and a quoted block's parse follow the window, with the arrival numbers before and after on both panes and the gates that hold them. ROADMAP.md flips the Shelf row and records the pull-forward.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The arrival gate asked for ten coloured rows, which a thirteen-row body cannot show; it now asks that every parsed line drew a class, since every line of the block is a fn. The spec carries the numbers from the run that passed.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@breferrari breferrari added the release This pull request may appear in CHANGELOG.md and the GitHub Release notes label Sep 30, 2026
breferrari and others added 8 commits September 30, 2026 22:31
A note's rows are one list of sections that the count sums and the build walks, so the two cannot disagree, and a unit test holds every shape, width and window to that. The answer's window functions report the rows they walked, so no part is counted before it is built. A quoted block's window is walked once for its pieces and its end, then given runs. The clamp finds a row's pins by index rather than by scanning them, and the box rows are windowed by a drain. The core's Quote doc says what it now does. The tall pane is read, not timed, and both budget gates share the coloured-rows count. The notes gates build their note with the file's own helper.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The read arm runs no warmup, so its first view was the app's plain first frame and parsed nothing.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two structural gates take names of four words, the row gate holds the built count to the note rows drawn, the section window and the sum have one name each, and three docblocks say what the code does rather than argue for it.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The I4 paragraph keeps the rule in the present tense with its author and its user-facing line, and names the bottom clamp's cost. RULINGS.md gains the entry with the measurements on both panes, the three rejected shapes and what is left as it is.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The pass sweeps a block it is not asked for after two frames, and a pinned note whose block rows are off the window was no longer asking, so a return re-parsed it. Every pinned answer now asks for its blocks each frame without parsing, which also raises the demand for an uncompiled grammar before the block is drawn, as it did before. A note whose own line leaves the pane is not pinned and is swept as before; its return now costs a screenful rather than the block, and a gate holds that. The arrival rig uses one highlighter, since every sample is a fresh digest, and the row gate's name is a label.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The highlighter marks an existing block live without hashing it, renews the demand for an uncompiled grammar, and starts an entry only where none exists, so a pinned answer behind the window costs the frame a lookup per block. Two gates hold the keep from both sides: a pinned note whose answer sits behind the window for two frames is not parsed again when its rows return, and a note whose line leaves the pane is swept and parses a screenful on return, no less and no more. The counter doc says what the pin keeps. The keep runs under the same condition as the rows it keeps for.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A held entry that was waiting on a grammar now compiled is rebuilt, as a draw would rebuild it, so it stops demanding a grammar it has; the demand itself is one function both the hold and the draw call. A gate in the warm suite holds a block behind the window for two frames and asserts its demand stands, then warms the grammar and asserts the demand ends and a draw finds colour. The hold says that a rewrite behind the window is seen when the block is next drawn.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One function says when a block waits on a grammar the warmer has compiled since or on one its fence no longer names, and both the hold and the draw rebuild on it, so a held block rewritten to another language behind the window is not held against the old grammar's demand. The warm gate holds the rebuild count as well as the demand, and takes a label for a name. The hold's doc says what a held block costs per case.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@breferrari
breferrari marked this pull request as ready for review September 30, 2026 21:19
Copilot AI balanced review requested due to automatic review settings September 30, 2026 21:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release This pull request may appear in CHANGELOG.md and the GitHub Release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A note's rows are built whole before the clamp trims them

2 participants