fix(tui): finish the MVU I/O eviction (Phase 0 follow-ups) #102

Merged
lerko merged 3 commits from fix/tui-mvu-followups into main 2026-06-11 15:54:22 +00:00
Owner

Completes the Phase 0 scope from the fresh-eyes review. PR #101 landed the tick-path refactor; this finishes the rest.

Theme global-style race

applyTheme mutated ~18 package-global lipgloss styles while every SSH session's tea.Program read them concurrently — pressing T or any new connection raced other sessions' View and bled themes across users. Styles now live in an immutable per-Model styles struct built by newStyles; the globals are deleted, so the race is structurally impossible. Free formatter helpers became Model methods.

Remaining keypress reads → Cmds

  • h history and the SLA view load via loadHistoryCmd/loadSLACmd with loading placeholders; replies for a different site/period are dropped
  • tabDataMsg carries a monotonic seq — out-of-order replies from slower earlier loads are dropped
  • detailDataMsg dropped when the cursor moved to another site
  • open detail panel refreshes on the tab-data cadence (detailRefreshCmd) instead of loading once on entry
  • initSiteHuhForm reads the m.alerts cache instead of hitting the store

All store writes → Cmds

Deletes, pause toggle, end-maintenance, theme/collapse prefs, and all four form submits run through writeCmdwriteDoneMsg, which logs failures and reloads tab data after the write lands. In-memory engine/model mutations stay in Update so rows react instantly. A failed site delete self-heals: the engine poll loop re-adds any site still in the DB.

Notes

  • Write Cmds capture snapshotted values only, never the model
  • A quit in the same instant as an in-flight write Cmd can drop the write (same window testAlertCmd already had); acceptable per the review plan
  • Mixed value/pointer receiver wart in Update left as-is — separate C finding, out of Phase 0 scope

Tests

go test -race ./... green, golangci-lint 0 issues. Seven new tests: seq-guard drops, stale history/SLA replies, async delete, write-error logging, detail refresh gating.

Completes the Phase 0 scope from the fresh-eyes review. PR #101 landed the tick-path refactor; this finishes the rest. ## Theme global-style race `applyTheme` mutated ~18 package-global lipgloss styles while every SSH session's `tea.Program` read them concurrently — pressing `T` or any new connection raced other sessions' `View` and bled themes across users. Styles now live in an immutable per-Model `styles` struct built by `newStyles`; the globals are deleted, so the race is structurally impossible. Free formatter helpers became Model methods. ## Remaining keypress reads → Cmds - `h` history and the SLA view load via `loadHistoryCmd`/`loadSLACmd` with loading placeholders; replies for a different site/period are dropped - `tabDataMsg` carries a monotonic seq — out-of-order replies from slower earlier loads are dropped - `detailDataMsg` dropped when the cursor moved to another site - open detail panel refreshes on the tab-data cadence (`detailRefreshCmd`) instead of loading once on entry - `initSiteHuhForm` reads the `m.alerts` cache instead of hitting the store ## All store writes → Cmds Deletes, pause toggle, end-maintenance, theme/collapse prefs, and all four form submits run through `writeCmd` → `writeDoneMsg`, which logs failures and reloads tab data after the write lands. In-memory engine/model mutations stay in Update so rows react instantly. A failed site delete self-heals: the engine poll loop re-adds any site still in the DB. ## Notes - Write Cmds capture snapshotted values only, never the model - A quit in the same instant as an in-flight write Cmd can drop the write (same window `testAlertCmd` already had); acceptable per the review plan - Mixed value/pointer receiver wart in Update left as-is — separate C finding, out of Phase 0 scope ## Tests `go test -race ./...` green, golangci-lint 0 issues. Seven new tests: seq-guard drops, stale history/SLA replies, async delete, write-error logging, detail refresh gating.
lerko added 3 commits 2026-06-11 15:44:17 +00:00
applyTheme mutated ~18 package-global lipgloss styles while every SSH
session's tea.Program read them concurrently from its own goroutine.
Pressing T or opening a new connection raced other sessions' View and
bled themes across users.

Styles now live in an immutable per-Model struct built by newStyles;
free formatter helpers that consumed the globals became Model methods.
The #101 refactor stopped at the tick path; 'h' history and the SLA
view still queried state changes synchronously in Update, freezing the
UI for up to busy_timeout on a contended DB. Both now load through
Cmds with loading placeholders.

Also closes the remaining staleness holes in the async data flow:
- tabDataMsg carries a sequence number; out-of-order replies from
  slower earlier loads are dropped instead of overwriting newer data
- history/SLA replies are dropped when the user has navigated to a
  different site or period
- the open detail panel refreshes on the tab-data cadence instead of
  loading once on entry and going stale
- initSiteHuhForm reads the m.alerts cache instead of hitting the store
fix(tui): move all store writes out of Update into tea.Cmds
CI / test (pull_request) Successful in 2m35s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
a3711c652c
Deletes, pause toggles, maintenance end, theme/collapse prefs, and all
four form submits wrote to the store synchronously on the UI goroutine;
with busy_timeout=5000 a contended DB froze input for up to 5s.

Writes now run through a writeCmd helper returning writeDoneMsg. The
in-memory engine/model mutations stay in Update so rows react
instantly; the reply logs failures and reloads tab data, so the UI
converges on what was actually written. Closures capture snapshotted
values only — never the model.
lerko merged commit a3711c652c into main 2026-06-11 15:54:22 +00:00
lerko deleted branch fix/tui-mvu-followups 2026-06-11 15:54:22 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lerkolabs/uptop#102