Compare commits

...

1 Commits

Author SHA1 Message Date
Molecule AI Dev Engineer B (MiniMax) 9619972ba9 fix(handlers): add mutex to captureBroadcaster to close data race
Issue: workspace_provision_concurrent_repro_test.go documents that
captureBroadcaster (the sequential-test stub in workspace_provision_test.go)
writes lastData without synchronization. Under `go test -race` + concurrent
goroutines (e.g. 7 simultaneous provisionWorkspaceCP calls), concurrent
writes to the unsync'd map are a true data race — detected by the race
detector on any test that exercises captureBroadcaster under -race.

Fix: add sync.Mutex to captureBroadcaster and guard lastData writes.
Also add sync import. The sequentialSafeBroadcaster in
workspace_provision_concurrent_repro_test.go already uses the same pattern.

Test: go test -race ./workspace-server/internal/handlers/ — no race.
2026-05-23 08:20:14 +00:00
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"testing"
"github.com/DATA-DOG/go-sqlmock"
@@ -1087,6 +1088,7 @@ func TestSeedInitialMemories_OversizedWithSecrets(t *testing.T) {
// captures, BroadcastOnly is a no-op since none of the
// WorkspaceHandler paths under test call it.
type captureBroadcaster struct {
mu sync.Mutex // guards lastData against concurrent writes (data race with go test -race)
lastData map[string]interface{}
}
@@ -1096,6 +1098,8 @@ type captureBroadcaster struct {
func (c *captureBroadcaster) BroadcastOnly(_ string, _ string, _ interface{}) {}
func (c *captureBroadcaster) RecordAndBroadcast(_ context.Context, _, _ string, data interface{}) error {
c.mu.Lock()
defer c.mu.Unlock()
if m, ok := data.(map[string]interface{}); ok {
// Shallow-copy so the caller can't mutate our capture.
cpy := make(map[string]interface{}, len(m))