diff --git a/CHANGELOG.md b/CHANGELOG.md index c29b4b690..b2fdd2855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Branch, changes and pull request on the cards and the footer catch up within a second of a commit, checkout or edit made outside a turn, where they could lag up to three seconds, and selecting a card reads its checkout at once instead of showing what the last read found. - Kiro CLI sessions follow `KIRO_HOME`. With it set, lich read Kiro's conversations, cost and context from `~/.kiro` while Kiro kept them under `KIRO_HOME`, so they showed nothing and History listed none; the agent that carries lich's hooks was installed where Kiro never looked, and a sandboxed session was not given the directory it writes into. ## [0.78.0] - 2026-10-22 diff --git a/native/cmd/lich-native/cardstate_test.go b/native/cmd/lich-native/cardstate_test.go index e28942263..2f65e1389 100644 --- a/native/cmd/lich-native/cardstate_test.go +++ b/native/cmd/lich-native/cardstate_test.go @@ -73,7 +73,8 @@ func fakeUnread(t *testing.T, calls chan<- []any) *backend.Client { _ = json.NewEncoder(w).Encode("") case "/rpc/project.Home": _ = json.NewEncoder(w).Encode(backend.Project{ID: "home", Path: "/home/me"}) - case "/rpc/store.AddProject", "/rpc/store.AddSession": + case "/rpc/store.AddProject", "/rpc/store.AddSession", + "/rpc/project.Diff", "/rpc/project.BaseStatus", "/rpc/project.PullRequest": _ = json.NewEncoder(w).Encode(nil) case "/rpc/store.SetSessionUnread": var args []api.SessionUnreadOptions diff --git a/native/cmd/lich-native/model.go b/native/cmd/lich-native/model.go index b7d851801..7cd45f7f2 100644 --- a/native/cmd/lich-native/model.go +++ b/native/cmd/lich-native/model.go @@ -15,8 +15,10 @@ import ( ) // gitPoll is how often each shown path's branch and changes are re-read. The -// backend pushes no git events. -const gitPoll = 3 * time.Second +// backend pushes no git events. One second because a read is a few +// milliseconds per checkout (measured 3-35ms), while 3s left a commit made in a +// terminal off the card for long enough to read as lag. +const gitPoll = time.Second // lastScreenKey is the global setting the last route is saved under, the one // record of which project the user was on. @@ -591,7 +593,14 @@ func (m *model) setActive(ctx context.Context, id string) { } readLeft := left != id && m.status.markSeen(left) readNew := m.focused && m.status.markSeen(id) + shown := "" + if p, i := m.sessionAt(id); p != nil { + shown = m.shownPath(*p, p.Sessions[i]) + } m.mu.Unlock() + if shown != "" { + go m.refreshPath(ctx, shown) + } if refocused { m.saveLayouts() } diff --git a/native/cmd/lich-native/stores_events_test.go b/native/cmd/lich-native/stores_events_test.go index 2b10f0d71..b238c710c 100644 --- a/native/cmd/lich-native/stores_events_test.go +++ b/native/cmd/lich-native/stores_events_test.go @@ -93,6 +93,33 @@ func TestSessionTouchedReadsGitNowAndNudgesTheDiff(t *testing.T) { } } +func TestSelectingACardReadsItsGitWithoutATick(t *testing.T) { + var mu sync.Mutex + head := "h1" + stub := &rpcStub{answer: func(method string, _ []json.RawMessage) (any, int) { + mu.Lock() + defer mu.Unlock() + switch method { + case "project.Diff": + return backend.DiffStats{Branch: "main", Head: head}, http.StatusOK + case "project.BaseStatus", "project.PullRequest": + return nil, http.StatusOK + } + return failing("unexpected " + method) + }} + m := newTestModel(stub.client(t)) + m.refreshGit(context.Background()) + + mu.Lock() + head = "h2" + mu.Unlock() + m.setActive(context.Background(), "s1") + + eventually(t, "the selected card reads the new head without a tick", func() bool { + return cardOf(t, m).git.Head == "h2" + }) +} + func TestSessionTouchedOfAnotherProjectWaitsForItsScreen(t *testing.T) { stub := &rpcStub{answer: func(string, []json.RawMessage) (any, int) { return failing("unexpected") }} m := newModel(stub.client(t), func() {})