Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ Every item on the deferral shelf below has a milestone here, so a milestone-filt
| ✅ | `SHEET_KEEP` names two things, so removing a gesture reassigns the never-drop set by rank | [#464](https://github.com/breferrari/vigia/issues/464) |
| ✅ | A release that removes a key can be filed as internal, and 0.42.0 was | [#467](https://github.com/breferrari/vigia/issues/467) |
| ✅ | A hidden path still wakes the watch. **Deferred by [#495](https://github.com/breferrari/vigia/issues/495): it changes what I1 measures** | [#501](https://github.com/breferrari/vigia/issues/501) |
| ⬜ | A note's rows are built whole before the clamp trims them. **Deferred by [#452](https://github.com/breferrari/vigia/issues/452): the steady frame is 12.557ms against 12.483ms as prose, and it is the frame an answer arrives on that scales** | [#530](https://github.com/breferrari/vigia/issues/530) |
| ✅ | A note's rows are built whole before the clamp trims them. **Deferred by [#452](https://github.com/breferrari/vigia/issues/452): the steady frame is 12.557ms against 12.483ms as prose, and it is the frame an answer arrives on that scales** | [#530](https://github.com/breferrari/vigia/issues/530) |
| ✅ | The caret row's weight is the one modifier a theme file cannot reach | [#195](https://github.com/breferrari/vigia/issues/195) |
| ⬜ | Three overlays are kept apart by hand in eight places. **Deferred by [#511](https://github.com/breferrari/vigia/issues/511) and again by [#508](https://github.com/breferrari/vigia/issues/508): a refactor of the two that predate the branches that found it.** [#519](https://github.com/breferrari/vigia/issues/519) is its duplicate, filed before this row was found and closed against it | [#515](https://github.com/breferrari/vigia/issues/515) |
| ⬜ | The sheet's tables are audited, not derived, so the keymap can still drift into them | [#312](https://github.com/breferrari/vigia/issues/312) |
Expand Down Expand Up @@ -580,6 +580,7 @@ Items that moved into an *earlier* phase than planned. Recorded for the same rea
| Exclude files frozen at watch start ([#621](https://github.com/breferrari/vigia/issues/621)) | Off the Shelf, 2026-09-30 | Filed by #619 and still real. Two stats a tick while events flow, none while idle. |
| Mixed release notes fail the bump ([#624](https://github.com/breferrari/vigia/issues/624)) | Off the Shelf, 2026-09-30 | Taken by the unattended loop, whose merged #617 and #622 it breaks the next release on. Workflow work, which the loop takes when it blocks releasing the loop's own PRs. |
| The heat projection's cost follows the file ([#55](https://github.com/breferrari/vigia/issues/55)) | Off the Shelf, 2026-09-30 | Taken by the unattended loop when `next.sh` ran dry. The row's reason was that nothing had ever taken it, which is not a reason that can hold. The fix is a slot beside the cached diff: a file's heat strip is projected once per computed diff and served from the frame while that diff is reused, so a frame pays a copy per drawn heading and the walk over the hunks is paid once per computed diff |
| A note's rows are built whole before the clamp trims them ([#530](https://github.com/breferrari/vigia/issues/530)) | Off the Shelf, 2026-09-30 | Taken by the reader's approval of the plan when `next.sh` ran dry. The row's reason was a cost statement, not a blocker: the arrival frame of a fenced answer scaled with the answer's length and was paid once. A note is now counted whole and built only where the clamp keeps it, and a quoted block is parsed as far as the last line the frame draws, so an arrival costs a screenful |

---

Expand Down
12 changes: 12 additions & 0 deletions RULINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@ The middle column **is** the floor. One real sibling pays the compile in full. T
>
> **Fixed 2026-09-04 ([#412](https://github.com/breferrari/vigia/issues/412)), which makes the line above false.** Every fresh diff now puts its height beside it, and the walk asks only the span, with one stat. A print that moved inside the settle margin keeps its height until the file settles, and the walk reads it once then. Re-reading inside the margin was #84's 20.71ms breach of I9. So the in-margin re-measure above became a deferral: one stat per file per tick, and one read when the file settles. That frame is asserted against I9.

## I4 — a note's rows and a quoted block's parse follow the window, 2026-09-30

Before [#530](https://github.com/breferrari/vigia/issues/530), `Pin::rows` built every row of every note twice a frame, once for the clamp's count and once to emit, and `answer_of` parsed every line of a fenced block on the frame the answer arrived. Measured in release on the note rig on 2026-09-12: a 2,000-line fenced answer cost 160.5ms on its arrival frame against 13.2ms as prose, and 10,000 lines cost 757.2ms. The steady frame held (12.557ms fenced against 12.483ms prose for twenty four-line answers), which is why the row sat on the Shelf as "paid once".

The mechanism: a note is one list of sections (`Pin::sections`) that the count sums and the build walks, so the clamp's exact count and the emitted rows cannot differ, and a unit test (`view::tests::count_matches_build`) holds every shape, width and window to that. The window functions report the rows they walked, so a part is not counted before it is built. A quoted block keeps its parser position (`Quote::parse`) and `Pass::quoted_to` fills it forward to the last line the window reaches; `HighlightStats::quoted_lines` counts the lines parsed. `View::built` counts the note rows a screen built.

Measured on the branch, release, with `arrival_holds_the_budget`: the 80x160 pane's fenced arrival went from **119.98ms p50 on `main` to 18.69ms**, against 13.14ms for the same bytes as prose, and it parsed 110 lines for 115 rows; the 80x24 pane reads **7.90ms fenced against 7.55ms prose**, 8 lines parsed for 13 rows. The tall pane's remainder is its window, a hundred-odd lines of grammar on top of a 13ms frame, so the gate times the ordinary pane and reads the tall one. The steady quoted gate moved from 12.557ms to 12.52ms fenced.

Three alternatives were rejected. A lazy clamp that never needs an exact count: the bottom clamp computes `dropped = total - height` and a box pulls the window by row sums, so the count must be exact, and only the build can be windowed. Parsing at placement with a line bound from the layout: placement runs before the clamp decides the window, so the bound would be circular; the rows the clamp keeps are the only position that knows it. Sharing the hunk's `Entry::fill_to`: a hunk has checkpoints, a rewind and three parse states because a line of it changes before every frame; a quote has one `Side` and is replaced whole on rewrite, so the ten-line fill loop is the only shape shared and a shared type would carry the hunk's machinery for nothing.

Left as it is: the bottom clamp on an answer taller than the pane parses to the last drawn row, which is the whole block, once, the same first touch the #51 bullet records for `G` into a hunk. The count walk is a width scan of the whole answer per frame, twice at most; `prose_pieces` rescans a paragraph's remainder per piece, which a 100 KB single line would feel, and which predates this change.

## I2 — why it is two numbers

> [!NOTE]
Expand Down
2 changes: 1 addition & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ A regression past any budget **fails the build.**
> **The measurement histories behind these rows are in [`RULINGS.md`](RULINGS.md)**
>
> - **I1** has a cost its row does not measure. `crossterm`'s mouse bundle sets `?1003h`, so pointer motion over the pane wakes the loop, and that cannot be reclaimed portably. **A batch that asks for nothing, moves no pointer mark and meets no deadline paints nothing**, so the wake is all it costs ([#154](https://github.com/breferrari/vigia/issues/154)).
> - **I4** was **narrowed 2026-08-01**. A height is hunk boundaries and line counts, not content. That is the row's first exception, and it is why #49's refusal of a repository-wide total still holds for sums over content. The height walk became **incremental 2026-08-04** (#101). Since 2026-09-04, a file that moved inside the settle margin keeps its height until it settles. It is then read once, on a wake at the margin's end ([#412](https://github.com/breferrari/vigia/issues/412), [#425](https://github.com/breferrari/vigia/issues/425)). The numbers and three rejected alternatives are in the ledger.
> - **I4** was **narrowed 2026-08-01**. A height is hunk boundaries and line counts, not content. That is the row's first exception, and it is why #49's refusal of a repository-wide total still holds for sums over content. The height walk became **incremental 2026-08-04** (#101). Since 2026-09-04, a file that moved inside the settle margin keeps its height until it settles. It is then read once, on a wake at the margin's end ([#412](https://github.com/breferrari/vigia/issues/412), [#425](https://github.com/breferrari/vigia/issues/425)). The numbers and three rejected alternatives are in the ledger. A note's rows and a quoted block's parse follow the window too ([#530](https://github.com/breferrari/vigia/issues/530)): a note is counted whole and built only where the clamp keeps it, and a block is parsed as far as the last line a frame draws, with the parser position kept beside the block. The frame a fenced answer arrives on costs a screenful of grammar, and a block drawn from its bottom costs the whole block once: on a tall pane either is a first touch outside I9, the shape the #51 bullet in §10 records for a hunk. Ruled 2026-09-30, reader. User-facing: an answer the agent quotes as code no longer freezes the pane for the length of the block when it arrives. Gated by `crates/vigia/tests/budgets.rs::arrival_holds_the_budget` on the ordinary pane and by `crates/vigia/tests/notes.rs::parse_follows_the_window` and `rows_follow_the_window` on the counters. The numbers and the alternatives are in the ledger.
> - **I2** is two numbers because re-diffing (`gix`, Phase 1) and re-highlighting (`syntect`, Phase 2) have different dependencies and phases. The 18.58ms-vs-3.27ms measurement that forced the split is in the ledger.
> - **I8** no longer names `SIGINT` for Ctrl-C, because raw mode makes Ctrl-C a key event, never a signal. The externally delivered `SIGINT` is **covered since 2026-08-08** ([#24](https://github.com/breferrari/vigia/issues/24)). It was taken for both tier-1 platforms in one issue, because the single-task version was Unix-only and [#16](https://github.com/breferrari/vigia/issues/16) had already rejected a guarantee that means different things on different targets. Measurement made that affordable: neither half adds a crate to any graph. The row's exclusions now read the same on both platforms.
> - **I3**'s scheduled soak runs 30 minutes a week on Linux over the 20 x 200 fixture. The fixture settles in about thirty seconds, so the window measures the plateau. *Ruled 2026-09-29, reader.* The full 24h runs by `workflow_dispatch` or locally. The sample count is fixed, so the statistic is computed the same way at any window.
Expand Down
Loading
Loading