detached 11bc196

fix(routes): repaint on every route snapshot

commit
11bc196354f2751195358d42b43f70ae06234e58
author
brice · · 2026-08-11 08:30 UTC
parent
3f78d59
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +16 -2
M internal/usecase/daemon/client_frame_routing.go +3 -2
@@ -132,10 +132,11 @@ func (d *Daemon) handleAttachmentClientFrame(token attachmentConnectionToken, f
132132 d.log.Warn("malformed recent route snapshot", "err", derr)
133133 break
134134 }
135- if !token.ac.setRouteSnapshot(snapshot) {
135+ replayIdentity := token.ac.setRouteSnapshot(snapshot)
136+ d.invalidateRender(token.sess, token.ac, false, "client_frame_routing.go:route-snapshot")
137+ if !replayIdentity {
136138 break
137139 }
138- d.invalidateRender(token.sess, token.ac, false, "client_frame_routing.go:route-snapshot")
139140 if err := d.sendCommittedRouteIdentityForAttachment(token); err != nil {
140141 d.detachOnAttachmentSendError(token, token.transport.transport)
141142 }
@@ -132,10 +132,11 @@ func (d *Daemon) handleAttachmentClientFrame(token attachmentConnectionToken, f
132 d.log.Warn("malformed recent route snapshot", "err", derr)132 d.log.Warn("malformed recent route snapshot", "err", derr)
133 break133 break
134 }134 }
135- if !token.ac.setRouteSnapshot(snapshot) {135+ replayIdentity := token.ac.setRouteSnapshot(snapshot)
136+ d.invalidateRender(token.sess, token.ac, false, "client_frame_routing.go:route-snapshot")
137+ if !replayIdentity {
136 break138 break
137 }139 }
138- d.invalidateRender(token.sess, token.ac, false, "client_frame_routing.go:route-snapshot")
139 if err := d.sendCommittedRouteIdentityForAttachment(token); err != nil {140 if err := d.sendCommittedRouteIdentityForAttachment(token); err != nil {
140 d.detachOnAttachmentSendError(token, token.transport.transport)141 d.detachOnAttachmentSendError(token, token.transport.transport)
141 }142 }
M internal/usecase/daemon/recent_route_snapshot_test.go +13 -0
@@ -62,6 +62,19 @@ func TestAttachmentStatusResolvesSubscribedRouteAttention(t *testing.T) {
6262 }
6363 }
6464
65+func TestRecentRouteSnapshotRepaintsWithoutDeferredIdentity(t *testing.T) {
66+ d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
67+ token := sess.attachmentToken(ac, ac.transport())
68+ ac.publishAttachmentCapability(token)
69+ ac.setRouteSnapshot(ports.RecentRouteSnapshot{Generation: 1})
70+
71+ payload, err := ports.MarshalRecentRouteSnapshot(ports.RecentRouteSnapshot{Generation: 2})
72+ require.NoError(t, err)
73+ require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
74+
75+ awaitFrame(t, sends, ports.MsgOutput)
76+}
77+
6578 func TestAttachmentRouteSnapshotCopiesPublishedEntries(t *testing.T) {
6679 _, _, ac, _ := newManualSessionWithPTYs(t, nil)
6780 entries := []ports.RecentRouteEntry{{Key: 1, Generation: 2, Name: "before", Kind: ports.RouteKindLocal}}
@@ -62,6 +62,19 @@ func TestAttachmentStatusResolvesSubscribedRouteAttention(t *testing.T) {
62 }62 }
63 }63 }
64 64
65+func TestRecentRouteSnapshotRepaintsWithoutDeferredIdentity(t *testing.T) {
66+ d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
67+ token := sess.attachmentToken(ac, ac.transport())
68+ ac.publishAttachmentCapability(token)
69+ ac.setRouteSnapshot(ports.RecentRouteSnapshot{Generation: 1})
70+
71+ payload, err := ports.MarshalRecentRouteSnapshot(ports.RecentRouteSnapshot{Generation: 2})
72+ require.NoError(t, err)
73+ require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
74+
75+ awaitFrame(t, sends, ports.MsgOutput)
76+}
77+
65 func TestAttachmentRouteSnapshotCopiesPublishedEntries(t *testing.T) {78 func TestAttachmentRouteSnapshotCopiesPublishedEntries(t *testing.T) {
66 _, _, ac, _ := newManualSessionWithPTYs(t, nil)79 _, _, ac, _ := newManualSessionWithPTYs(t, nil)
67 entries := []ports.RecentRouteEntry{{Key: 1, Generation: 2, Name: "before", Kind: ports.RouteKindLocal}}80 entries := []ports.RecentRouteEntry{{Key: 1, Generation: 2, Name: "before", Kind: ports.RouteKindLocal}}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close