From f34aedff059921464fdc6b53629df5ce98e21c12 Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 16:33:14 +0000 Subject: [PATCH] fix(ax): make runner status writes explicit Co-Authored-By: Virgil --- pkg/runner/paths.go | 26 +++++++++++++++++--------- pkg/runner/paths_example_test.go | 4 +++- pkg/runner/paths_test.go | 14 ++++++++------ pkg/runner/queue.go | 5 ++++- pkg/runner/runner_test.go | 10 +++++----- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/pkg/runner/paths.go b/pkg/runner/paths.go index 95935b5..f24511a 100644 --- a/pkg/runner/paths.go +++ b/pkg/runner/paths.go @@ -102,16 +102,24 @@ func ReadStatusResult(wsDir string) core.Result { // WriteStatus writes `status.json` for one workspace directory. // -// runner.WriteStatus("/srv/core/workspace/core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) -func WriteStatus(wsDir string, st *WorkspaceStatus) { - if st == nil { - return +// r := runner.WriteStatus("/srv/core/workspace/core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) +// core.Println(r.OK) +func WriteStatus(wsDir string, status *WorkspaceStatus) core.Result { + if status == nil { + return core.Result{Value: core.E("runner.WriteStatus", "status is required", nil), OK: false} } - status := agenticWorkspaceStatusFromRunner(st) - if status == nil { - return + agenticStatus := agenticWorkspaceStatusFromRunner(status) + if agenticStatus == nil { + return core.Result{Value: core.E("runner.WriteStatus", "status conversion failed", nil), OK: false} } - status.UpdatedAt = time.Now() - fs.WriteAtomic(agentic.WorkspaceStatusPath(wsDir), core.JSONMarshalString(status)) + agenticStatus.UpdatedAt = time.Now() + if r := fs.WriteAtomic(agentic.WorkspaceStatusPath(wsDir), core.JSONMarshalString(agenticStatus)); !r.OK { + err, _ := r.Value.(error) + if err == nil { + return core.Result{Value: core.E("runner.WriteStatus", "failed to write status", nil), OK: false} + } + return core.Result{Value: core.E("runner.WriteStatus", "failed to write status", err), OK: false} + } + return core.Result{OK: true} } diff --git a/pkg/runner/paths_example_test.go b/pkg/runner/paths_example_test.go index d2f146a..79300b7 100644 --- a/pkg/runner/paths_example_test.go +++ b/pkg/runner/paths_example_test.go @@ -61,16 +61,18 @@ func ExampleWriteStatus() { dir := fsys.TempDir("runner-paths") defer fsys.DeleteAll(dir) - WriteStatus(dir, &WorkspaceStatus{ + result := WriteStatus(dir, &WorkspaceStatus{ Status: "running", Agent: "codex", Repo: "go-io", }) + core.Println(result.OK) st, err := ReadStatus(dir) core.Println(err == nil) core.Println(st.Status) // Output: // true + // true // running } diff --git a/pkg/runner/paths_test.go b/pkg/runner/paths_test.go index 7aa1ea2..261297e 100644 --- a/pkg/runner/paths_test.go +++ b/pkg/runner/paths_test.go @@ -140,7 +140,7 @@ func TestPaths_ReadStatus_Ugly(t *testing.T) { func TestPaths_WriteStatus_Good(t *testing.T) { wsDir := t.TempDir() - WriteStatus(wsDir, &WorkspaceStatus{ + result := WriteStatus(wsDir, &WorkspaceStatus{ Status: "running", Agent: "codex", Repo: "go-io", @@ -148,6 +148,7 @@ func TestPaths_WriteStatus_Good(t *testing.T) { Branch: "agent/ax-cleanup", Runs: 1, }) + assert.True(t, result.OK) st, err := ReadStatus(wsDir) require.NoError(t, err) @@ -165,7 +166,8 @@ func TestPaths_WriteStatus_Good(t *testing.T) { func TestPaths_WriteStatus_Bad(t *testing.T) { wsDir := t.TempDir() - WriteStatus(wsDir, nil) + result := WriteStatus(wsDir, nil) + assert.False(t, result.OK) assert.False(t, agentic.LocalFs().Read(agentic.WorkspaceStatusPath(wsDir)).OK) } @@ -173,13 +175,13 @@ func TestPaths_WriteStatus_Bad(t *testing.T) { func TestPaths_WriteStatus_Ugly(t *testing.T) { wsDir := t.TempDir() - WriteStatus(wsDir, &WorkspaceStatus{ + assert.True(t, WriteStatus(wsDir, &WorkspaceStatus{ Status: "running", Agent: "codex", Repo: "go-io", Task: "First run", - }) - WriteStatus(wsDir, &WorkspaceStatus{ + }).OK) + assert.True(t, WriteStatus(wsDir, &WorkspaceStatus{ Status: "completed", Agent: "claude", Repo: "go-io", @@ -187,7 +189,7 @@ func TestPaths_WriteStatus_Ugly(t *testing.T) { Branch: "agent/ax-cleanup", StartedAt: time.Now(), Runs: 3, - }) + }).OK) st, err := ReadStatus(wsDir) require.NoError(t, err) diff --git a/pkg/runner/queue.go b/pkg/runner/queue.go index 684e0ec..d1c95d7 100644 --- a/pkg/runner/queue.go +++ b/pkg/runner/queue.go @@ -278,7 +278,10 @@ func (s *Service) drainOne() bool { st.Status = "running" st.PID = pid st.Runs++ - WriteStatus(wsDir, st) + if r := WriteStatus(wsDir, st); !r.OK { + core.Error("drainOne: failed to write workspace status", "workspace", wsName, "reason", core.Sprint(r.Value)) + continue + } s.TrackWorkspace(wsName, st) core.Info("drainOne: spawned", "pid", pid, "workspace", wsName) diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index ddef1c1..ddbefb0 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -345,12 +345,12 @@ func TestRunner_HydrateWorkspaces_Good_DeepWorkspaceName(t *testing.T) { wsDir := core.JoinPath(root, "workspace", "core", "go-io", "task-5") fs.EnsureDir(wsDir) - WriteStatus(wsDir, &WorkspaceStatus{ + require.True(t, WriteStatus(wsDir, &WorkspaceStatus{ Status: "running", Agent: "codex", Repo: "go-io", PID: 99999999, - }) + }).OK) svc := New() svc.hydrateWorkspaces() @@ -367,7 +367,7 @@ func TestRunner_HydrateWorkspaces_Good_DeepWorkspaceName(t *testing.T) { func TestRunner_WriteReadStatus_Good(t *testing.T) { dir := t.TempDir() st := &WorkspaceStatus{Status: "running", Agent: "codex", Repo: "go-io", PID: 999} - WriteStatus(dir, st) + require.True(t, WriteStatus(dir, st).OK) got, err := ReadStatus(dir) require.NoError(t, err) @@ -383,8 +383,8 @@ func TestRunner_ReadStatus_Bad_NoFile(t *testing.T) { func TestRunner_WriteReadStatus_Ugly_OverwriteExisting(t *testing.T) { dir := t.TempDir() - WriteStatus(dir, &WorkspaceStatus{Status: "running"}) - WriteStatus(dir, &WorkspaceStatus{Status: "completed"}) + require.True(t, WriteStatus(dir, &WorkspaceStatus{Status: "running"}).OK) + require.True(t, WriteStatus(dir, &WorkspaceStatus{Status: "completed"}).OK) got, err := ReadStatus(dir) require.NoError(t, err)