From b8466fb56d8af2621f8035a5d2ac2b12880cb9e7 Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 19:14:14 +0000 Subject: [PATCH] fix(ax): make workspace extraction Result-native Co-Authored-By: Virgil --- pkg/agentic/commands.go | 7 +++-- pkg/agentic/prep.go | 9 ++++-- pkg/lib/lib.go | 40 ++++++++++++++++++------ pkg/lib/lib_example_test.go | 4 +-- pkg/lib/lib_test.go | 62 +++++++++++++++++++------------------ pkg/setup/setup.go | 10 ++++-- 6 files changed, 84 insertions(+), 48 deletions(-) diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index 585ce7b..72058aa 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -224,8 +224,11 @@ func (s *PrepSubsystem) cmdExtract(opts core.Options) core.Result { } core.Print(nil, "extracting template %q to %s", tmpl, target) - if err := lib.ExtractWorkspace(tmpl, target, data); err != nil { - return core.Result{Value: err, OK: false} + if result := lib.ExtractWorkspace(tmpl, target, data); !result.OK { + if err, ok := result.Value.(error); ok { + return core.Result{Value: core.E("agentic.cmdExtract", core.Concat("extract workspace template ", tmpl), err), OK: false} + } + return core.Result{Value: core.E("agentic.cmdExtract", core.Concat("extract workspace template ", tmpl), nil), OK: false} } fsys := s.Core().Fs() diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index 8fae4b7..c1bfd05 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -466,12 +466,17 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } // Extract default workspace template (go.work etc.) - lib.ExtractWorkspace("default", wsDir, &lib.WorkspaceData{ + if result := lib.ExtractWorkspace("default", wsDir, &lib.WorkspaceData{ Repo: input.Repo, Branch: "", Task: input.Task, Agent: input.Agent, - }) + }); !result.OK { + if err, ok := result.Value.(error); ok { + return nil, PrepOutput{}, core.E("prepWorkspace", "extract default workspace template", err) + } + return nil, PrepOutput{}, core.E("prepWorkspace", "extract default workspace template", nil) + } if !resumed { // Clone repo into repo/ diff --git a/pkg/lib/lib.go b/pkg/lib/lib.go index 4a6fbb4..a35e503 100644 --- a/pkg/lib/lib.go +++ b/pkg/lib/lib.go @@ -18,7 +18,8 @@ // r := lib.Task("code/review") // r.Value.(string) // r := lib.Persona("secops/dev") // r.Value.(string) // r := lib.Flow("go") // r.Value.(string) -// lib.ExtractWorkspace("default", "/tmp/ws", data) +// r := lib.ExtractWorkspace("default", "/tmp/ws", data) +// core.Println(r.OK) package lib import ( @@ -264,32 +265,51 @@ type WorkspaceData struct { // ExtractWorkspace creates an agent workspace from a template. // Template names: "default", "security", "review". // -// lib.ExtractWorkspace("default", "/tmp/ws", &lib.WorkspaceData{ +// r := lib.ExtractWorkspace("default", "/tmp/ws", &lib.WorkspaceData{ // Repo: "go-io", Task: "fix tests", Agent: "codex", // }) -func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error { +// core.Println(r.OK) +func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) core.Result { if result := ensureMounted(); !result.OK { if err, ok := result.Value.(error); ok { - return err + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("mount workspace template ", tmplName), err), + OK: false, + } + } + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("mount workspace template ", tmplName), nil), + OK: false, } - return core.E("lib.ExtractWorkspace", core.Concat("mount workspace template ", tmplName), nil) } r := workspaceFS.Sub(tmplName) if !r.OK { if err, ok := r.Value.(error); ok { - return err + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("template not found: ", tmplName), err), + OK: false, + } + } + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("template not found: ", tmplName), nil), + OK: false, } - return core.E("ExtractWorkspace", core.Concat("template not found: ", tmplName), nil) } result := core.Extract(r.Value.(*core.Embed).FS(), targetDir, data) if !result.OK { if err, ok := result.Value.(error); ok { - return err + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("extract workspace template ", tmplName), err), + OK: false, + } + } + return core.Result{ + Value: core.E("lib.ExtractWorkspace", core.Concat("extract workspace template ", tmplName), nil), + OK: false, } - return core.E("lib.ExtractWorkspace", core.Concat("extract workspace template ", tmplName), nil) } - return nil + return core.Result{Value: targetDir, OK: true} } // WorkspaceFile reads a single file from a workspace template. diff --git a/pkg/lib/lib_example_test.go b/pkg/lib/lib_example_test.go index c11cb61..b455a1f 100644 --- a/pkg/lib/lib_example_test.go +++ b/pkg/lib/lib_example_test.go @@ -103,9 +103,9 @@ func ExampleExtractWorkspace() { dir := (&core.Fs{}).NewUnrestricted().TempDir("example-ws") defer (&core.Fs{}).NewUnrestricted().DeleteAll(dir) - err := ExtractWorkspace("default", dir, &WorkspaceData{ + r := ExtractWorkspace("default", dir, &WorkspaceData{ Repo: "go-io", Task: "fix tests", }) - core.Println(err == nil) + core.Println(r.OK) // Output: true } diff --git a/pkg/lib/lib_test.go b/pkg/lib/lib_test.go index 81eaa13..f37b68a 100644 --- a/pkg/lib/lib_test.go +++ b/pkg/lib/lib_test.go @@ -53,6 +53,30 @@ func corruptLibMountForTest(t *testing.T) { }) } +func requireExtractWorkspaceOK(t *testing.T, result core.Result) string { + t.Helper() + if !result.OK { + t.Fatalf("ExtractWorkspace failed: %v", result.Value) + } + path, ok := result.Value.(string) + if !ok { + t.Fatalf("ExtractWorkspace returned %T, want string", result.Value) + } + return path +} + +func requireExtractWorkspaceError(t *testing.T, result core.Result) error { + t.Helper() + if result.OK { + t.Fatalf("ExtractWorkspace unexpectedly succeeded: %#v", result.Value) + } + err, ok := result.Value.(error) + if !ok { + t.Fatalf("ExtractWorkspace returned %T, want error", result.Value) + } + return err +} + // --- Prompt --- func TestLib_Prompt_Good(t *testing.T) { @@ -489,10 +513,7 @@ func TestLib_ExtractWorkspace_Good(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "test-repo", Task: "test task"} - err := ExtractWorkspace("default", dir, data) - if err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) for _, name := range []string{"CODEX.md", "CLAUDE.md", "PROMPT.md", "TODO.md", "CONTEXT.md", "go.work"} { if !testFs.Exists(core.JoinPath(dir, name)) { @@ -505,10 +526,7 @@ func TestLib_ExtractWorkspaceSubdirs_Good(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "test-repo", Task: "test task"} - err := ExtractWorkspace("default", dir, data) - if err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) refDir := core.JoinPath(dir, ".core", "reference") if !testFs.IsDir(refDir) { @@ -535,10 +553,7 @@ func TestLib_ExtractWorkspaceTemplate_Good(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "my-repo", Task: "fix the bug"} - err := ExtractWorkspace("default", dir, data) - if err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) r := testFs.Read(core.JoinPath(dir, "TODO.md")) if !r.OK { @@ -550,27 +565,18 @@ func TestLib_ExtractWorkspaceTemplate_Good(t *testing.T) { } func TestLib_ExtractWorkspace_Bad(t *testing.T) { - err := ExtractWorkspace("missing-template", t.TempDir(), &WorkspaceData{Repo: "test-repo"}) - if err == nil { - t.Fatal("ExtractWorkspace should fail for an unknown template") - } + requireExtractWorkspaceError(t, ExtractWorkspace("missing-template", t.TempDir(), &WorkspaceData{Repo: "test-repo"})) } func TestLib_ExtractWorkspace_Ugly(t *testing.T) { - err := ExtractWorkspace("default", t.TempDir(), nil) - if err == nil { - t.Fatal("ExtractWorkspace should fail when template data is nil") - } + requireExtractWorkspaceError(t, ExtractWorkspace("default", t.TempDir(), nil)) } func TestLib_ExtractWorkspace_Good_AXConventions(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "test-repo", Task: "align AX docs"} - err := ExtractWorkspace("default", dir, data) - if err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) r := testFs.Read(core.JoinPath(dir, "CODEX.md")) if !r.OK { @@ -624,9 +630,7 @@ func TestLib_ExtractWorkspace_Good_ReferenceHeaders(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "test-repo", Task: "carry SPDX headers into workspace references"} - if err := ExtractWorkspace("default", dir, data); err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) refDir := core.JoinPath(dir, ".core", "reference") goFiles := core.PathGlob(core.JoinPath(refDir, "*.go")) @@ -643,9 +647,7 @@ func TestLib_ExtractWorkspace_Good_ReferenceUsageExamples(t *testing.T) { dir := t.TempDir() data := &WorkspaceData{Repo: "test-repo", Task: "carry AX usage examples into workspace references"} - if err := ExtractWorkspace("default", dir, data); err != nil { - t.Fatalf("ExtractWorkspace failed: %v", err) - } + requireExtractWorkspaceOK(t, ExtractWorkspace("default", dir, data)) cases := map[string][]string{ core.JoinPath(dir, ".core", "reference", "array.go"): { diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go index c259030..efc287a 100644 --- a/pkg/setup/setup.go +++ b/pkg/setup/setup.go @@ -134,9 +134,15 @@ func (s *Service) scaffoldTemplate(opts Options, projType ProjectType, tmplName return core.Result{Value: opts.Path, OK: true} } - if err := lib.ExtractWorkspace(tmplName, opts.Path, data); err != nil { + if result := lib.ExtractWorkspace(tmplName, opts.Path, data); !result.OK { + if err, ok := result.Value.(error); ok { + return core.Result{ + Value: core.E("setup.scaffoldTemplate", core.Concat("extract workspace template ", tmplName), err), + OK: false, + } + } return core.Result{ - Value: core.E("setup.scaffoldTemplate", core.Concat("extract workspace template ", tmplName), err), + Value: core.E("setup.scaffoldTemplate", core.Concat("extract workspace template ", tmplName), nil), OK: false, } }