detached 05e5006

fix(client): detach gracefully on controlling-terminal hangup

A terminal hangup or termination signal killed the attach client with the
default disposition, skipping transport close and the performance-trace
flush. Bind the attach context to SIGHUP/SIGTERM/SIGINT so teardown
unwinds the pumps, closes the transport, and pairs the in-flight receive
span. Raw mode disables ISIG, so interactive Ctrl+C is unaffected.
commit
05e5006fe6c63262c6b74c759c9fa5552626cadf
author
brice · · 2026-07-18 16:28 UTC
committer
brice · · 2026-07-18 16:35 UTC
parent
156ea64
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +9 -0
M internal/app/run.go +9 -0
@@ -480,6 +480,15 @@ func runDaemon() (retErr error) {
480480 // attach loop. Logging goes to the shared file: the client must never write
481481 // to the console while the terminal is raw.
482482 func runAttach(ctx context.Context, intent uint8, name, remoteTarget string) (retErr error) {
483+ // Treat a controlling-terminal hangup or termination request as a graceful
484+ // detach rather than an abrupt process death. Cancelling the context unwinds
485+ // the client's pumps, closes its transport, and lets the deferred trace closer
486+ // flush the in-flight receive's end mark — so teardown never truncates a span.
487+ // Raw mode disables ISIG, so catching SIGINT here does not affect interactive
488+ // Ctrl+C, which the daemon delivers to the remote shell as a normal keystroke.
489+ ctx, stop := signal.NotifyContext(ctx, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
490+ defer stop()
491+
483492 log, logCloser, err := configureLogging(logging.Client, false)
484493 if err != nil {
485494 return err
@@ -480,6 +480,15 @@ func runDaemon() (retErr error) {
480 // attach loop. Logging goes to the shared file: the client must never write480 // attach loop. Logging goes to the shared file: the client must never write
481 // to the console while the terminal is raw.481 // to the console while the terminal is raw.
482 func runAttach(ctx context.Context, intent uint8, name, remoteTarget string) (retErr error) {482 func runAttach(ctx context.Context, intent uint8, name, remoteTarget string) (retErr error) {
483+ // Treat a controlling-terminal hangup or termination request as a graceful
484+ // detach rather than an abrupt process death. Cancelling the context unwinds
485+ // the client's pumps, closes its transport, and lets the deferred trace closer
486+ // flush the in-flight receive's end mark — so teardown never truncates a span.
487+ // Raw mode disables ISIG, so catching SIGINT here does not affect interactive
488+ // Ctrl+C, which the daemon delivers to the remote shell as a normal keystroke.
489+ ctx, stop := signal.NotifyContext(ctx, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
490+ defer stop()
491+
483 log, logCloser, err := configureLogging(logging.Client, false)492 log, logCloser, err := configureLogging(logging.Client, false)
484 if err != nil {493 if err != nil {
485 return err494 return err

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