* fix: persist per-workspace dir overrides across agent recreation In multi-workspace mode, /dir overrides were lost after daemon restart or workspace pool eviction because getOrCreateWorkspaceAgent always recreated agents with work_dir=workspace root, ignoring the override stored in project state. Changes: - Add WorkspaceDirOverrides map to ProjectStateStore for per-workspace dir override persistence - Apply persisted override when recreating workspace agents in getOrCreateWorkspaceAgent - Store workspace-specific overrides in dirApply when in multi-workspace mode (set and reset) - Auto-clear invalid overrides (missing/non-directory paths) Backward compatible: existing work_dir_override field preserved for single-workspace mode. * fix: use per-channel dir overrides instead of per-workspace Two channels bound to the same workspace were sharing /dir overrides. Change the override key from workspace path to interactiveKey (workspace:sessionKey) so each channel maintains independent dir state. - Remove override from getOrCreateWorkspaceAgent (shared agent layer) - Add applyChannelDirOverride helper, called in handleMessage and commandContext (per-channel routing layer) - dirApply now stores/clears overrides keyed by interactiveKey * refactor: centralize multi-workspace context resolution Use a shared workspaceContext helper so commandContext and message routing build the same workspace agent/session/interactiveKey state in one place. This keeps the per-channel dir override fix unchanged while removing duplicate override application logic. * fix: route multi-workspace dir overrides by effective work dir Avoid mutating shared workspace agents when applying per-channel /dir overrides. Resolve the effective work dir from project state and route each channel to an agent context keyed by that dir instead. This removes cross-channel work_dir races while preserving existing /dir persistence and reset behavior.
68 lines
2.4 KiB
Go
68 lines
2.4 KiB
Go
package core
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestProjectState_SaveLoadAndClear(t *testing.T) {
|
|
statePath := filepath.Join(t.TempDir(), "projects", "demo.state.json")
|
|
|
|
store := NewProjectStateStore(statePath)
|
|
store.SetWorkDirOverride("/tmp/demo")
|
|
store.Save()
|
|
|
|
reloaded := NewProjectStateStore(statePath)
|
|
if got := reloaded.WorkDirOverride(); got != "/tmp/demo" {
|
|
t.Fatalf("WorkDirOverride() = %q, want %q", got, "/tmp/demo")
|
|
}
|
|
|
|
reloaded.ClearWorkDirOverride()
|
|
reloaded.Save()
|
|
|
|
cleared := NewProjectStateStore(statePath)
|
|
if got := cleared.WorkDirOverride(); got != "" {
|
|
t.Fatalf("WorkDirOverride() after clear = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestWorkspaceDirOverride(t *testing.T) {
|
|
statePath := filepath.Join(t.TempDir(), "projects", "demo.state.json")
|
|
workspaceA := "/tmp/workspace-a"
|
|
workspaceB := "/tmp/workspace-b"
|
|
|
|
store := NewProjectStateStore(statePath)
|
|
store.SetWorkDirOverride("/tmp/global")
|
|
store.SetWorkspaceDirOverride(workspaceA, "/tmp/workspace-a/override")
|
|
store.SetWorkspaceDirOverride(workspaceB, "/tmp/workspace-b/override")
|
|
store.Save()
|
|
|
|
reloaded := NewProjectStateStore(statePath)
|
|
if got := reloaded.WorkDirOverride(); got != "/tmp/global" {
|
|
t.Fatalf("WorkDirOverride() = %q, want %q", got, "/tmp/global")
|
|
}
|
|
if got := reloaded.WorkspaceDirOverride(workspaceA); got != "/tmp/workspace-a/override" {
|
|
t.Fatalf("WorkspaceDirOverride(%q) = %q, want %q", workspaceA, got, "/tmp/workspace-a/override")
|
|
}
|
|
if got := reloaded.WorkspaceDirOverride(workspaceB); got != "/tmp/workspace-b/override" {
|
|
t.Fatalf("WorkspaceDirOverride(%q) = %q, want %q", workspaceB, got, "/tmp/workspace-b/override")
|
|
}
|
|
if got := reloaded.WorkspaceDirOverride("/tmp/missing"); got != "" {
|
|
t.Fatalf("WorkspaceDirOverride(missing) = %q, want empty", got)
|
|
}
|
|
|
|
reloaded.ClearWorkspaceDirOverride(workspaceA)
|
|
reloaded.Save()
|
|
|
|
cleared := NewProjectStateStore(statePath)
|
|
if got := cleared.WorkDirOverride(); got != "/tmp/global" {
|
|
t.Fatalf("WorkDirOverride() after workspace clear = %q, want %q", got, "/tmp/global")
|
|
}
|
|
if got := cleared.WorkspaceDirOverride(workspaceA); got != "" {
|
|
t.Fatalf("WorkspaceDirOverride(%q) after clear = %q, want empty", workspaceA, got)
|
|
}
|
|
if got := cleared.WorkspaceDirOverride(workspaceB); got != "/tmp/workspace-b/override" {
|
|
t.Fatalf("WorkspaceDirOverride(%q) after clearing other workspace = %q, want %q", workspaceB, got, "/tmp/workspace-b/override")
|
|
}
|
|
}
|