TUI never refreshes: state is frozen until you press r #73

Closed
opened 2026-08-12 16:57:00 +00:00 by a73x · 1 comment
a73x commented 2026-08-12 16:57:00 +00:00 (Migrated from git.a73x.sh)

The dashboard's event loop polls for keyboard input, not for repository changes:

terminal.draw(|frame| ui(frame, app, Some(repo)))?;
if event::poll(Duration::from_millis(100))? { ... }

app.reload(repo) runs in exactly three places — the r key, and the two branches after n creates an issue. Nothing watches refs, nothing polls the DAG, nothing re-reads on a timer. Open the dashboard and it shows the state from whenever you last pressed r, indefinitely.

This matters most for the workflow the project is being built for. The natural way to use a dashboard is to leave it open while agents work. An agent lands three patches and closes two issues; the screen says otherwise and looks perfectly healthy. There is no marker, no timestamp, no indication that what you are reading is old.

That is the same class of defect as the cache-only merged status fixed in 46368211: state that is wrong in a way nothing on screen admits. The fix there was to stop deriving and start recording; here it is simply to notice.

Cheap approach, in rough order of effort:

  • Show when the data was loaded, so a stale screen at least says so. This alone removes the silent-wrong property.
  • Reload on a timer — the loop already wakes every 100ms, so a check every second or two costs nothing measurable next to the redraw already happening.
  • Cheaper than a full reload: stat the collab refs (or compare the tips) and only refold when something moved. list_issues/list_patches walk every DAG, so a blind timer reload on a large repo is not free.

Do not make the TUI write during a read while fixing this — reloading is a fold, and from_ref_uncached is already the read-only path.

Found while reviewing what the TUI can do (5c62b43b).


Migrated from git-collab issue 59ab40e89f0efbbe5fed9259c2ce595ed573ddcb.

Original author: a73x dev@a73x.sh.

Close reason: fixed on main

The dashboard's event loop polls for **keyboard input**, not for repository changes: ```rust terminal.draw(|frame| ui(frame, app, Some(repo)))?; if event::poll(Duration::from_millis(100))? { ... } ``` `app.reload(repo)` runs in exactly three places — the `r` key, and the two branches after `n` creates an issue. Nothing watches refs, nothing polls the DAG, nothing re-reads on a timer. Open the dashboard and it shows the state from whenever you last pressed `r`, indefinitely. **This matters most for the workflow the project is being built for.** The natural way to use a dashboard is to leave it open while agents work. An agent lands three patches and closes two issues; the screen says otherwise and looks perfectly healthy. There is no marker, no timestamp, no indication that what you are reading is old. That is the same class of defect as the cache-only merged status fixed in 46368211: state that is wrong in a way nothing on screen admits. The fix there was to stop deriving and start recording; here it is simply to notice. Cheap approach, in rough order of effort: - Show when the data was loaded, so a stale screen at least says so. This alone removes the silent-wrong property. - Reload on a timer — the loop already wakes every 100ms, so a check every second or two costs nothing measurable next to the redraw already happening. - Cheaper than a full reload: stat the collab refs (or compare the tips) and only refold when something moved. `list_issues`/`list_patches` walk every DAG, so a blind timer reload on a large repo is not free. Do not make the TUI write during a read while fixing this — reloading is a fold, and `from_ref_uncached` is already the read-only path. Found while reviewing what the TUI can do (5c62b43b). --- Migrated from git-collab issue `59ab40e89f0efbbe5fed9259c2ce595ed573ddcb`. Original author: a73x <dev@a73x.sh>. Close reason: fixed on main
a73x commented 2026-08-12 17:20:54 +00:00 (Migrated from git.a73x.sh)

Refined 2026-08-12. Decision: notify, do not reload.

A timer compares collab ref tips — cheap, a ref read, no fold. When they move, show a banner and let the user press r. Never reload under them.

The reasoning is the purpose settled on 094b055b: the TUI is a review surface, so the situation you will actually be in is writing a comment while three agents land patches. Rows appearing, vanishing or reordering mid-review is worse than being briefly out of date, and "reload only when idle" cannot distinguish reading a long diff from having walked away.

DASHBOARD                    as of 14:02
┌──────────────────────────────────────┐
│ ● 4 new events · r to reload         │
└──────────────────────────────────────┘

Requirements:

  • An always-visible as of timestamp, present whether or not anything changed. This alone removes the silent-wrong property that makes the current behaviour a bug rather than a limitation — a stale screen that admits it is stale is merely old.
  • Detection is a tip comparison, not a fold. list_issues/list_patches walk every DAG, so a blind timer reload is not free on a large repo. Compare refs/collab/** tips and only refold on r.
  • The TUI's own writes must not count as external. After n, c, R or x the app reloads anyway; the banner must not then announce the user's own comment as news. Re-baseline the tip snapshot as part of that reload.
  • Never write during a read. The tip check is a ref read; keep it one.
  • The count is of events, not objects — "4 new events" is honest and cheap; "2 issues changed" requires knowing which, which requires the fold you are avoiding.

Interval is an implementation choice; the loop already wakes every 100ms for input, so anything from 1–5s costs nothing measurable next to the redraw already happening.

**Refined 2026-08-12. Decision: notify, do not reload.** A timer compares collab ref tips — cheap, a ref read, no fold. When they move, show a banner and let the user press `r`. Never reload under them. The reasoning is the purpose settled on 094b055b: the TUI is a **review surface**, so the situation you will actually be in is writing a comment while three agents land patches. Rows appearing, vanishing or reordering mid-review is worse than being briefly out of date, and "reload only when idle" cannot distinguish reading a long diff from having walked away. ``` DASHBOARD as of 14:02 ┌──────────────────────────────────────┐ │ ● 4 new events · r to reload │ └──────────────────────────────────────┘ ``` Requirements: - **An always-visible `as of` timestamp**, present whether or not anything changed. This alone removes the silent-wrong property that makes the current behaviour a bug rather than a limitation — a stale screen that admits it is stale is merely old. - **Detection is a tip comparison, not a fold.** `list_issues`/`list_patches` walk every DAG, so a blind timer reload is not free on a large repo. Compare `refs/collab/**` tips and only refold on `r`. - **The TUI's own writes must not count as external.** After `n`, `c`, `R` or `x` the app reloads anyway; the banner must not then announce the user's own comment as news. Re-baseline the tip snapshot as part of that reload. - **Never write during a read.** The tip check is a ref read; keep it one. - The count is of events, not objects — "4 new events" is honest and cheap; "2 issues changed" requires knowing which, which requires the fold you are avoiding. Interval is an implementation choice; the loop already wakes every 100ms for input, so anything from 1–5s costs nothing measurable next to the redraw already happening.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
a73x/git-collab#73
No description provided.