@@ -642,17 +642,36 @@ func protoWS(path, dataDir, clientID string) proto.Workspace {
642642 return proto.Workspace{Path: path, DataDir: dataDir, ClientID: clientID}
643643}
644644
645+// syncBuffer is a thread-safe buffer that can be safely read and written
646+// from multiple goroutines.
647+type syncBuffer struct {
648+ mu sync.Mutex
649+ buf bytes.Buffer
650+}
651+
652+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
653+ sb.mu.Lock()
654+ defer sb.mu.Unlock()
655+ return sb.buf.Write(p)
656+}
657+
658+func (sb *syncBuffer) String() string {
659+ sb.mu.Lock()
660+ defer sb.mu.Unlock()
661+ return sb.buf.String()
662+}
663+
645664// captureDebugLogs installs a buffer-backed slog handler at Debug
646665// level for the duration of the test, returning the buffer. The
647666// previous default handler is restored via t.Cleanup.
648−func captureDebugLogs(t *testing.T) *bytes.Buffer {
667+func captureDebugLogs(t *testing.T) *syncBuffer {
649668 t.Helper()
650− var buf bytes.Buffer
669+ var sb syncBuffer
651670 prev := slog.Default()
652− handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
671+ handler := slog.NewTextHandler(&sb, &slog.HandlerOptions{Level: slog.LevelDebug})
653672 slog.SetDefault(slog.New(handler))
654673 t.Cleanup(func() { slog.SetDefault(prev) })
655− return &buf
674+ return &sb
656675}
657676
658677// xdgIsolated points HOME and XDG_* variables at fresh tempdirs so