From eb7a6e31db68b4692395b3f6cfd5f4c05c84091b Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 16:52:49 +0000 Subject: [PATCH] fix(ax): normalise monitor repo paths deterministically Co-Authored-By: Virgil --- pkg/monitor/monitor.go | 5 ----- pkg/monitor/sync.go | 2 +- pkg/monitor/sync_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index a365b61..4c40e69 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -33,11 +33,6 @@ func brainKeyPath(home string) string { return core.JoinPath(home, ".claude", "brain.key") } -func monitorPath(path string) string { - ds := core.Env("DS") - return core.Replace(core.Replace(path, "\\", ds), "/", ds) -} - func monitorHomeDir() string { if d := core.Env("CORE_HOME"); d != "" { return d diff --git a/pkg/monitor/sync.go b/pkg/monitor/sync.go index 99805e2..5fc2dc4 100644 --- a/pkg/monitor/sync.go +++ b/pkg/monitor/sync.go @@ -63,7 +63,7 @@ func (m *Subsystem) syncRepos() string { var pulled []string for _, repo := range checkin.Changed { // Sanitise repo name to prevent path traversal from API response - repoName := core.PathBase(monitorPath(repo.Repo)) + repoName := core.PathBase(core.Replace(repo.Repo, "\\", "/")) if repoName == "." || repoName == ".." || repoName == "" { continue } diff --git a/pkg/monitor/sync_test.go b/pkg/monitor/sync_test.go index 2c54958..3533675 100644 --- a/pkg/monitor/sync_test.go +++ b/pkg/monitor/sync_test.go @@ -124,6 +124,49 @@ func TestSync_SyncRepos_Good_PullsChangedRepo(t *testing.T) { assert.Contains(t, msg, "test-repo") } +func TestSync_SyncRepos_Good_NormalisesWindowsRepoPath(t *testing.T) { + remoteDir := core.JoinPath(t.TempDir(), "remote") + fs.EnsureDir(remoteDir) + run(t, remoteDir, "git", "init", "--bare") + + codeDir := t.TempDir() + repoDir := core.JoinPath(codeDir, "test-repo") + run(t, codeDir, "git", "clone", remoteDir, "test-repo") + run(t, repoDir, "git", "checkout", "-b", "main") + fs.Write(core.JoinPath(repoDir, "README.md"), "# test") + run(t, repoDir, "git", "add", ".") + run(t, repoDir, "git", "commit", "-m", "init") + run(t, repoDir, "git", "push", "-u", "origin", "main") + + clone2Parent := t.TempDir() + tmpClone := core.JoinPath(clone2Parent, "clone2") + run(t, clone2Parent, "git", "clone", remoteDir, "clone2") + run(t, tmpClone, "git", "checkout", "main") + fs.Write(core.JoinPath(tmpClone, "new.go"), "package main\n") + run(t, tmpClone, "git", "add", ".") + run(t, tmpClone, "git", "commit", "-m", "agent work") + run(t, tmpClone, "git", "push", "origin", "main") + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp := CheckinResponse{ + Changed: []ChangedRepo{{Repo: "core\\test-repo", Branch: "main", SHA: "abc"}}, + Timestamp: time.Now().Unix() + 100, + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(core.JSONMarshalString(resp))) + })) + defer srv.Close() + + setupAPIEnv(t, srv.URL) + t.Setenv("CODE_PATH", codeDir) + + mon := New() + mon.ServiceRuntime = testMon.ServiceRuntime + msg := mon.syncRepos() + assert.Contains(t, msg, "Synced 1 repo(s)") + assert.Contains(t, msg, "core\\test-repo") +} + func TestSync_SyncRepos_Good_SkipsDirtyRepo(t *testing.T) { remoteDir := core.JoinPath(t.TempDir(), "remote") fs.EnsureDir(remoteDir)