fix(agentic): expand watch workspace prefixes

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 00:11:17 +00:00
parent 318cff805d
commit 8828e89e62
2 changed files with 107 additions and 4 deletions

View file

@ -54,10 +54,7 @@ func (s *PrepSubsystem) watch(ctx context.Context, request *mcp.CallToolRequest,
start := time.Now()
deadline := start.Add(timeout)
workspaceNames := input.Workspaces
if len(workspaceNames) == 0 {
workspaceNames = s.findActiveWorkspaces()
}
workspaceNames := s.watchWorkspaceNames(input.Workspaces)
if len(workspaceNames) == 0 {
return nil, WatchOutput{
@ -180,6 +177,66 @@ func (s *PrepSubsystem) watch(ctx context.Context, request *mcp.CallToolRequest,
}, nil
}
func (s *PrepSubsystem) watchWorkspaceNames(workspaces []string) []string {
if len(workspaces) == 0 {
return s.findActiveWorkspaces()
}
statusPaths := WorkspaceStatusPaths()
if len(statusPaths) == 0 {
return nil
}
seen := make(map[string]bool)
add := func(names []string, workspaceName string) []string {
if workspaceName == "" || seen[workspaceName] {
return names
}
seen[workspaceName] = true
return append(names, workspaceName)
}
var workspaceNames []string
for _, rawWorkspace := range workspaces {
requested := core.Trim(rawWorkspace)
if requested == "" {
continue
}
requested = core.TrimSuffix(requested, "/")
matched := false
for _, statusPath := range statusPaths {
workspaceDir := core.PathDir(statusPath)
workspaceName := WorkspaceName(workspaceDir)
if workspaceName == requested || workspaceDir == requested {
workspaceNames = add(workspaceNames, workspaceName)
matched = true
}
}
prefix := requested
if !core.HasSuffix(prefix, "/") {
prefix = core.Concat(prefix, "/")
}
for _, statusPath := range statusPaths {
workspaceDir := core.PathDir(statusPath)
workspaceName := WorkspaceName(workspaceDir)
if core.HasPrefix(workspaceName, prefix) || core.HasPrefix(workspaceDir, prefix) {
workspaceNames = add(workspaceNames, workspaceName)
matched = true
}
}
if !matched {
workspaceNames = add(workspaceNames, requested)
}
}
return workspaceNames
}
// active := s.findActiveWorkspaces()
// if len(active) == 0 { return nil }
func (s *PrepSubsystem) findActiveWorkspaces() []string {

View file

@ -220,6 +220,52 @@ func TestWatch_Watch_Good_AutoDiscoversAndCompletes(t *testing.T) {
assert.Equal(t, "https://forge.lthn.ai/core/go-io/pulls/42", out.Completed[0].PRURL)
}
func TestWatch_Watch_Good_ExpandsParentWorkspacePrefix(t *testing.T) {
root := t.TempDir()
t.Setenv("CORE_WORKSPACE", root)
writeWatchStatus(root, "core/go-io/task-41", WorkspaceStatus{
Status: "running",
Repo: "go-io",
Agent: "codex",
})
writeWatchStatus(root, "core/go-io/task-42", WorkspaceStatus{
Status: "running",
Repo: "go-io",
Agent: "codex",
})
go func() {
time.Sleep(50 * time.Millisecond)
writeWatchStatus(root, "core/go-io/task-41", WorkspaceStatus{
Status: "completed",
Repo: "go-io",
Agent: "codex",
})
time.Sleep(50 * time.Millisecond)
writeWatchStatus(root, "core/go-io/task-42", WorkspaceStatus{
Status: "completed",
Repo: "go-io",
Agent: "codex",
})
}()
s := newPrepWithProcess()
_, out, err := s.watch(context.Background(), nil, WatchInput{
Workspaces: []string{"core/go-io"},
PollInterval: 1,
Timeout: 2,
})
assert.NoError(t, err)
assert.True(t, out.Success)
assert.Empty(t, out.Failed)
assert.Len(t, out.Completed, 2)
assert.ElementsMatch(t, []string{"core/go-io/task-41", "core/go-io/task-42"}, []string{
out.Completed[0].Workspace,
out.Completed[1].Workspace,
})
}
func TestWatch_Watch_Bad_CancelledContext(t *testing.T) {
root := t.TempDir()
t.Setenv("CORE_WORKSPACE", root)