Skip to content

fix: project a file's heat strip once instead of on every frame - #628

Merged
breferrari merged 5 commits into
mainfrom
issue-55-heat-once-per-diff
Sep 30, 2026
Merged

breferrari merged 5 commits into
mainfrom
issue-55-heat-once-per-diff

Conversation

@breferrari

@breferrari breferrari commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

A drawn heading's heat strip is now projected once per computed diff and served from the frame while that diff is reused. Before, heat_of walked every hunk line of every drawn file on every frame, which SPEC.md §10 recorded as the one drawn thing whose cost followed the file rather than the window. Closes #55. Plan: #55 (comment).

The mechanism is one slot beside the cached diff. Frame::diff_with returns the change, the diff and a value derived from the diff in one call, so the diff walk and the list walk spend no second stat. The slot is dropped with the diff, so nothing new can go stale and nothing new grows. FrameStats::derived counts the derivations.

Scope: full. The change class script reads the diff as code.

Labelled release on the reader's call of 2026-09-30, now the rule in #629: the change is in vigia-core and ships in the binary. Two public additions in vigia-core, the derived field on FrameStats and Frame::diff_with, widen the crate's API, so the bump this ships in is not a patch.

Test plan

  • crates/vigia/tests/reads.rs::heat_once_per_diff: a second screen over settled diffs derives 0; after one file is rewritten, the next screen derives exactly as many strips as the rewrite recomputed diffs (one), and that file's strip follows its new diff.
  • crates/vigia-core/tests/frame.rs::derived_once_per_diff: two asks over one reused diff derive once and recompute nothing; a longer file derives again and the value follows the new diff.
  • Mutation battery, .claude/scripts/mutate.mjs, run on the final code: killed never serve the kept value, never keep the value, keep the slot across a recompute, count nothing. A fifth, the shell projecting per frame while ignoring the frame's value, survives the two gates and is refused by CI's RUSTFLAGS=-D warnings as an unused parameter.
  • Rail frame, a_frame_beside_a_rail_holds_the_frame_budget, release, 200x40 pane drawing 36 pinned rows, two rounds each interleaved: p50 5.69ms and 5.65ms on main, 5.67ms and 5.64ms here; p99 7.05ms and 7.11ms against 6.74ms and 6.96ms, all against I9's 16ms. The frame did not move measurably; the shape did.
  • Local: clippy clean under -D warnings; vigia-core unit tests 101 passed; register 49 and package 9 passed after the rebase. The full suite and the budgets run in CI.

Review

  • /simplify: folded the derive-and-count into one path; insert_str in the gate. Skipped: a once-per-diff derive for the one file the viewport is scrolled inside on a pane too short for a list, because avoiding it needs a second frame call, which costs a stat; a TypeId-keyed map for the slot, because there is one consumer and the doc states the limit.
  • /two-axis-review: the third screen's bound is now taken from the run (diffs the rewrite recomputed) rather than a constant; the gate's name dropped a word; the closed bullet names its gate and the budget fixture and says what holds. Skipped: a ruling line on the closed bullet, because the three earlier closed bullets in §10 carry numbers and no ruling line; the typed slot, because it needs the strip's constant in vigia-core, which the plan rejected.
  • /code-review high: the doc on diff_with now states one value per diff; the failure path's note says its derive is uncounted on purpose; the roadmap row takes its siblings' column value. Skipped: the third hash lookup in diff_with (one short-string hash on a path the numbers above put within noise, and the borrow shape is why); printing derived in the timings example, which drives the core with diff and never calls diff_with.
  • Copilot: no review; the reviewer reported its quota limit reached.

Release note

Release-note: Each file's heat strip is projected once per change instead of on every frame.

🤖 Generated with Claude Code

@breferrari breferrari added internal This pull request must not appear in CHANGELOG.md or the GitHub Release notes and removed internal This pull request must not appear in CHANGELOG.md or the GitHub Release notes labels Sep 30, 2026
@breferrari breferrari changed the title chore: project the heat strip once per diff fix: project a file's heat strip once instead of on every frame Sep 30, 2026
@breferrari breferrari added the release This pull request may appear in CHANGELOG.md and the GitHub Release notes label Sep 30, 2026
breferrari added a commit that referenced this pull request Sep 30, 2026
* Label a PR by what ships

Step 7 chose the label by what a user can see, so a change to shipped code with no visible symptom, such as a frame-path cost fix, fell to internal by the tie-break. The label now follows what the binary is built from: src under crates, a dependency, a theme or install is release; process, CI, skills, the spec, the roadmap, tests and gates alone are internal. Ruled 2026-09-30, reader, on #628.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Decide the label by path in every file that states it

The skill's rule now reads off the changed paths: crates/*/src, crates/*/assets or a dependency line is release, a comment or refactor included; everything else is internal. CONTRIBUTING.md and the pull request template said release meant a user can see the change, which contradicted the skill on the case that prompted it, so both say the same thing in plain English now.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Say the same rule in all three files

The skill names install again, which the public files never stopped naming, excludes dev-dependencies from the dependency clause, and carries the ruling in the house form with its User-facing line. The template and CONTRIBUTING.md stop saying that a release note describes what a user sees and that a refactor is internal, since neither holds under the path rule.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
breferrari and others added 5 commits September 30, 2026 21:09
A drawn heading's heat strip was projected on every frame by walking every hunk line of its file, which was the one drawn thing whose cost followed the file rather than the window. The frame now keeps a value derived from a cached diff beside that diff, so the projection is paid when the diff is computed and served while the diff is reused.

Frame::diff_with hands back the diff and the derived value from one call, so the list and the diff walk spend no second stat. FrameStats::derived counts the derivations, and two gates hold it: a second screen over settled diffs derives nothing, and a rewritten file derives exactly once with a strip that follows its new diff.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
SPEC.md section 10 closes the bullet that tracked #55, with the mechanism and the rail frame measured on both sides of the change. The paragraph that told the next pass how to re-measure it goes with it. ROADMAP.md flips the Shelf row and records the pull-forward.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
diff_with resolves the cached entry or the failure first, then derives and counts once, so the two match arms no longer repeat the count. A failed read is not cached, so its diff derives without counting: the counter says once per diff, and a value nothing keeps is not one.

The reads gate inserts its edit with insert_str rather than an empty-range replace.

Release-note: Each file's heat strip is projected once per change instead of on every frame.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The reads gate takes the number of diffs the rewrite recomputed from the frame's own counters and holds the next screen's projections to it, rather than to the constant one. The gate's name drops a word. The closed bullet names the gate and the budget fixture it was measured with, and says what holds: a drawn heading costs a copy, and the walk over the hunks is paid once per computed diff.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The doc on diff_with states the contract in full: one value per diff, a second derivation of the same type is served the first's value, and another type replaces it. The failure path's note says the derive there is uncounted on purpose. The roadmap's pull-forward row takes the column value its siblings use.

Release-note: none

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@breferrari
breferrari force-pushed the issue-55-heat-once-per-diff branch from 2d6c517 to fd18d81 Compare September 30, 2026 19:10
@breferrari
breferrari marked this pull request as ready for review September 30, 2026 19:10
Copilot AI balanced review requested due to automatic review settings September 30, 2026 19:10

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.

@breferrari
breferrari merged commit 06e5721 into main Sep 30, 2026
22 checks passed
@breferrari
breferrari deleted the issue-55-heat-once-per-diff branch September 30, 2026 19:21
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.

The heat projection's cost follows the file rather than the window

2 participants