[agent/claude:opus] DX audit and fix. 1) Review CLAUDE.md — update any outdate... #1

Merged
Virgil merged 1 commit from agent/dx-audit-and-fix--1--review-claude-md into main 2026-03-17 09:00:40 +00:00
5 changed files with 259 additions and 6 deletions

5
go.mod
View file

@ -11,12 +11,15 @@ require (
forge.lthn.ai/core/go-ws v0.2.3
forge.lthn.ai/core/gui v0.1.3
forge.lthn.ai/core/mcp v0.3.2
github.com/stretchr/testify v1.11.1
github.com/wailsapp/wails/v3 v3.0.0-alpha.74
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
modernc.org/libc v1.70.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
@ -28,7 +31,7 @@ require (
dario.cat/mergo v1.0.2 // indirect
forge.lthn.ai/core/go-ai v0.1.11 // indirect
forge.lthn.ai/core/go-io v0.1.5 // indirect
forge.lthn.ai/core/go-log v0.0.4 // indirect
forge.lthn.ai/core/go-log v0.0.4
forge.lthn.ai/core/go-rag v0.1.9 // indirect
forge.lthn.ai/core/go-webview v0.1.5 // indirect
github.com/99designs/gqlgen v0.17.88 // indirect

33
main_test.go Normal file
View file

@ -0,0 +1,33 @@
package main
import (
"testing"
"forge.lthn.ai/core/config"
"github.com/stretchr/testify/assert"
)
func TestGuiEnabled_Good_NilConfig(t *testing.T) {
// nil config should fall through to display detection.
result := guiEnabled(nil)
// On macOS/Windows this returns true; on Linux it depends on DISPLAY.
// Just verify it doesn't panic.
_ = result
}
func TestGuiEnabled_Good_WithConfig(t *testing.T) {
cfg, _ := config.New()
// Fresh config has no gui.enabled key — should fall through to OS detection.
result := guiEnabled(cfg)
_ = result
}
func TestStaticAssetGroup_Good(t *testing.T) {
s := &staticAssetGroup{
name: "test-assets",
basePath: "/assets/test",
dir: "/tmp",
}
assert.Equal(t, "test-assets", s.Name())
assert.Equal(t, "/assets/test", s.BasePath())
}

86
providers_test.go Normal file
View file

@ -0,0 +1,86 @@
package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"forge.lthn.ai/core/api/pkg/provider"
"forge.lthn.ai/core/go-scm/manifest"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProvidersAPI_Name(t *testing.T) {
api := NewProvidersAPI(nil, nil)
assert.Equal(t, "providers-api", api.Name())
}
func TestProvidersAPI_BasePath(t *testing.T) {
api := NewProvidersAPI(nil, nil)
assert.Equal(t, "/api/v1/providers", api.BasePath())
}
func TestProvidersAPI_List_Good_Empty(t *testing.T) {
gin.SetMode(gin.TestMode)
reg := provider.NewRegistry()
rm := NewRuntimeManager(nil)
api := NewProvidersAPI(reg, rm)
router := gin.New()
rg := router.Group(api.BasePath())
api.RegisterRoutes(rg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/providers", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var resp providersResponse
err := json.Unmarshal(w.Body.Bytes(), &resp)
require.NoError(t, err)
assert.Empty(t, resp.Providers)
}
func TestProvidersAPI_List_Good_WithRuntimeProviders(t *testing.T) {
gin.SetMode(gin.TestMode)
reg := provider.NewRegistry()
rm := NewRuntimeManager(nil)
// Simulate a runtime provider.
rm.providers = append(rm.providers, &RuntimeProvider{
Dir: "/tmp/test",
Port: 9999,
Manifest: &manifest.Manifest{
Code: "test-provider",
Name: "Test Provider",
Version: "0.1.0",
Namespace: "test",
},
})
api := NewProvidersAPI(reg, rm)
router := gin.New()
rg := router.Group(api.BasePath())
api.RegisterRoutes(rg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/providers", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var resp providersResponse
err := json.Unmarshal(w.Body.Bytes(), &resp)
require.NoError(t, err)
require.Len(t, resp.Providers, 1)
assert.Equal(t, "test-provider", resp.Providers[0].Name)
assert.Equal(t, "test", resp.Providers[0].BasePath)
assert.Equal(t, "active", resp.Providers[0].Status)
}

View file

@ -15,6 +15,7 @@ import (
"forge.lthn.ai/core/api"
"forge.lthn.ai/core/api/pkg/provider"
coreerr "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/manifest"
"forge.lthn.ai/core/go-scm/marketplace"
"github.com/gin-gonic/gin"
@ -63,7 +64,7 @@ func (rm *RuntimeManager) StartAll(ctx context.Context) error {
dir := defaultProvidersDir()
discovered, err := marketplace.DiscoverProviders(dir)
if err != nil {
return fmt.Errorf("runtime: discover providers: %w", err)
return coreerr.E("runtime.StartAll", "discover providers", err)
}
if len(discovered) == 0 {
@ -93,7 +94,7 @@ func (rm *RuntimeManager) startProvider(ctx context.Context, dp marketplace.Disc
// Assign a free port.
port, err := findFreePort()
if err != nil {
return nil, fmt.Errorf("find free port: %w", err)
return nil, coreerr.E("runtime.startProvider", "find free port", err)
}
// Resolve binary path.
@ -114,7 +115,7 @@ func (rm *RuntimeManager) startProvider(ctx context.Context, dp marketplace.Disc
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("start binary %s: %w", binaryPath, err)
return nil, coreerr.E("runtime.startProvider", fmt.Sprintf("start binary %s", binaryPath), err)
}
// Wait for health check.
@ -122,7 +123,7 @@ func (rm *RuntimeManager) startProvider(ctx context.Context, dp marketplace.Disc
if err := waitForHealth(healthURL, 10*time.Second); err != nil {
// Kill the process if health check fails.
_ = cmd.Process.Kill()
return nil, fmt.Errorf("health check failed for %s: %w", m.Code, err)
return nil, coreerr.E("runtime.startProvider", fmt.Sprintf("health check failed for %s", m.Code), err)
}
// Register proxy provider.
@ -248,7 +249,7 @@ func waitForHealth(url string, timeout time.Duration) error {
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("health check timed out after %s: %s", timeout, url)
return coreerr.E("runtime.waitForHealth", fmt.Sprintf("timed out after %s: %s", timeout, url), nil)
}
// staticAssetGroup is a simple RouteGroup that serves static files.

130
runtime_test.go Normal file
View file

@ -0,0 +1,130 @@
package main
import (
"context"
"net/http"
"net/http/httptest"
"os/exec"
"testing"
"time"
"forge.lthn.ai/core/go-scm/manifest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindFreePort_Good(t *testing.T) {
port, err := findFreePort()
require.NoError(t, err)
assert.Greater(t, port, 0)
assert.Less(t, port, 65536)
}
func TestFindFreePort_UniquePerCall(t *testing.T) {
port1, err := findFreePort()
require.NoError(t, err)
port2, err := findFreePort()
require.NoError(t, err)
// Two consecutive calls should very likely return different ports.
// (Not guaranteed, but effectively always true.)
assert.NotEqual(t, port1, port2)
}
func TestWaitForHealth_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
err := waitForHealth(srv.URL, 5*time.Second)
assert.NoError(t, err)
}
func TestWaitForHealth_Bad_Timeout(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
}))
defer srv.Close()
err := waitForHealth(srv.URL, 500*time.Millisecond)
require.Error(t, err)
assert.Contains(t, err.Error(), "timed out")
}
func TestWaitForHealth_Bad_NoServer(t *testing.T) {
err := waitForHealth("http://127.0.0.1:1", 500*time.Millisecond)
require.Error(t, err)
assert.Contains(t, err.Error(), "timed out")
}
func TestDefaultProvidersDir_Good(t *testing.T) {
dir := defaultProvidersDir()
assert.Contains(t, dir, ".core")
assert.Contains(t, dir, "providers")
}
func TestRuntimeManager_List_Good_Empty(t *testing.T) {
rm := NewRuntimeManager(nil)
infos := rm.List()
assert.Empty(t, infos)
}
func TestRuntimeManager_List_Good_WithProviders(t *testing.T) {
rm := NewRuntimeManager(nil)
rm.providers = []*RuntimeProvider{
{
Dir: "/tmp/test-provider",
Port: 12345,
Manifest: &manifest.Manifest{
Code: "test-svc",
Name: "Test Service",
Version: "1.0.0",
Namespace: "test",
},
},
}
infos := rm.List()
require.Len(t, infos, 1)
assert.Equal(t, "test-svc", infos[0].Code)
assert.Equal(t, "Test Service", infos[0].Name)
assert.Equal(t, "1.0.0", infos[0].Version)
assert.Equal(t, "test", infos[0].Namespace)
assert.Equal(t, 12345, infos[0].Port)
assert.Equal(t, "/tmp/test-provider", infos[0].Dir)
}
func TestRuntimeManager_StopAll_Good_Empty(t *testing.T) {
rm := NewRuntimeManager(nil)
// Should not panic with no providers.
rm.StopAll()
assert.Empty(t, rm.providers)
}
func TestRuntimeManager_StopAll_Good_WithProcess(t *testing.T) {
// Start a real process so we can test graceful stop.
cmd := exec.CommandContext(context.Background(), "sleep", "60")
require.NoError(t, cmd.Start())
rm := NewRuntimeManager(nil)
rm.providers = []*RuntimeProvider{
{
Manifest: &manifest.Manifest{Code: "sleeper"},
Cmd: cmd,
},
}
rm.StopAll()
assert.Nil(t, rm.providers)
}
func TestRuntimeManager_StartAll_Good_EmptyDir(t *testing.T) {
rm := NewRuntimeManager(nil)
// StartAll with a non-existent providers dir should return an error
// because the default dir won't have providers (at most it logs and returns nil).
err := rm.StartAll(context.Background())
// Depending on whether ~/.core/providers/ exists, this either returns
// nil (no providers found) or an error (dir doesn't exist).
// Either outcome is acceptable — no panic.
_ = err
}