From 09c25b99757d2b0141fb0004849acfb102ac5e01 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:38:00 +0000 Subject: [PATCH 1/7] chore: replace interface{} with any (Go 1.18+ alias) Co-Authored-By: Claude Opus 4.6 --- pkg/cache/cache.go | 4 ++-- pkg/framework/core/interfaces.go | 6 +++--- pkg/session/parser.go | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 60aefb9..bc5dc5a 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -67,7 +67,7 @@ func (c *Cache) Path(key string) string { } // Get retrieves a cached item if it exists and hasn't expired. -func (c *Cache) Get(key string, dest interface{}) (bool, error) { +func (c *Cache) Get(key string, dest any) (bool, error) { path := c.Path(key) dataStr, err := c.medium.Read(path) @@ -98,7 +98,7 @@ func (c *Cache) Get(key string, dest interface{}) (bool, error) { } // Set stores an item in the cache. -func (c *Cache) Set(key string, data interface{}) error { +func (c *Cache) Set(key string, data any) error { path := c.Path(key) // Ensure parent directory exists diff --git a/pkg/framework/core/interfaces.go b/pkg/framework/core/interfaces.go index 8d587d2..a347ebb 100644 --- a/pkg/framework/core/interfaces.go +++ b/pkg/framework/core/interfaces.go @@ -44,15 +44,15 @@ type Option func(*Core) error // Message is the interface for all messages that can be sent through the Core's IPC system. // Any struct can be a message, allowing for structured data to be passed between services. // Used with ACTION for fire-and-forget broadcasts. -type Message interface{} +type Message any // Query is the interface for read-only requests that return data. // Used with QUERY (first responder) or QUERYALL (all responders). -type Query interface{} +type Query any // Task is the interface for requests that perform side effects. // Used with PERFORM (first responder executes). -type Task interface{} +type Task any // TaskWithID is an optional interface for tasks that need to know their assigned ID. // This is useful for tasks that want to report progress back to the frontend. diff --git a/pkg/session/parser.go b/pkg/session/parser.go index 6304189..4c3dd87 100644 --- a/pkg/session/parser.go +++ b/pkg/session/parser.go @@ -54,7 +54,7 @@ type contentBlock struct { Text string `json:"text,omitempty"` Input json.RawMessage `json:"input,omitempty"` ToolUseID string `json:"tool_use_id,omitempty"` - Content interface{} `json:"content,omitempty"` + Content any `json:"content,omitempty"` IsError *bool `json:"is_error,omitempty"` } @@ -340,7 +340,7 @@ func extractToolInput(toolName string, raw json.RawMessage) string { } // Fallback: show raw JSON keys - var m map[string]interface{} + var m map[string]any if json.Unmarshal(raw, &m) == nil { var parts []string for k := range m { @@ -353,21 +353,21 @@ func extractToolInput(toolName string, raw json.RawMessage) string { return "" } -func extractResultContent(content interface{}) string { +func extractResultContent(content any) string { switch v := content.(type) { case string: return v - case []interface{}: + case []any: var parts []string for _, item := range v { - if m, ok := item.(map[string]interface{}); ok { + if m, ok := item.(map[string]any); ok { if text, ok := m["text"].(string); ok { parts = append(parts, text) } } } return strings.Join(parts, "\n") - case map[string]interface{}: + case map[string]any: if text, ok := v["text"].(string); ok { return text } From d570c87efca8975969db13f44e62b0ece32a8b51 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:38:38 +0000 Subject: [PATCH 2/7] =?UTF-8?q?chore:=20fmt.Errorf(static)=20=E2=86=92=20e?= =?UTF-8?q?rrors.New?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- pkg/coredeno/lifecycle.go | 3 ++- pkg/framework/core/service_manager.go | 3 ++- pkg/i18n/internal/validate/main.go | 3 ++- pkg/i18n/service.go | 5 +++-- pkg/manifest/sign.go | 3 ++- pkg/process/runner.go | 6 +++--- pkg/repos/registry.go | 3 ++- pkg/session/video.go | 3 ++- pkg/webview/actions.go | 7 ++++--- pkg/webview/angular.go | 7 ++++--- pkg/webview/cdp.go | 5 +++-- pkg/webview/webview.go | 25 +++++++++++++------------ pkg/ws/ws.go | 3 ++- 13 files changed, 44 insertions(+), 32 deletions(-) diff --git a/pkg/coredeno/lifecycle.go b/pkg/coredeno/lifecycle.go index 0a9a885..a603fd1 100644 --- a/pkg/coredeno/lifecycle.go +++ b/pkg/coredeno/lifecycle.go @@ -2,6 +2,7 @@ package coredeno import ( "context" + "errors" "fmt" "os" "os/exec" @@ -14,7 +15,7 @@ func (s *Sidecar) Start(ctx context.Context, args ...string) error { defer s.mu.Unlock() if s.cmd != nil { - return fmt.Errorf("coredeno: already running") + return errors.New("coredeno: already running") } // Ensure socket directory exists with owner-only permissions diff --git a/pkg/framework/core/service_manager.go b/pkg/framework/core/service_manager.go index 80c208f..9c4b0bd 100644 --- a/pkg/framework/core/service_manager.go +++ b/pkg/framework/core/service_manager.go @@ -1,6 +1,7 @@ package core import ( + "errors" "fmt" "sync" ) @@ -27,7 +28,7 @@ func newServiceManager() *serviceManager { // It also appends to startables/stoppables if the service implements those interfaces. func (m *serviceManager) registerService(name string, svc any) error { if name == "" { - return fmt.Errorf("core: service name cannot be empty") + return errors.New("core: service name cannot be empty") } m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/i18n/internal/validate/main.go b/pkg/i18n/internal/validate/main.go index 5e0d942..d295c57 100644 --- a/pkg/i18n/internal/validate/main.go +++ b/pkg/i18n/internal/validate/main.go @@ -19,6 +19,7 @@ package main import ( "encoding/json" + "errors" "fmt" "go/ast" "go/parser" @@ -101,7 +102,7 @@ func findProjectRoot() (string, error) { } parent := filepath.Dir(dir) if parent == dir { - return "", fmt.Errorf("could not find go.mod in any parent directory") + return "", errors.New("could not find go.mod in any parent directory") } dir = parent } diff --git a/pkg/i18n/service.go b/pkg/i18n/service.go index 91d1181..07c38a3 100644 --- a/pkg/i18n/service.go +++ b/pkg/i18n/service.go @@ -4,6 +4,7 @@ package i18n import ( "embed" "encoding/json" + "errors" "fmt" "io/fs" "path" @@ -118,7 +119,7 @@ func NewWithLoader(loader Loader, opts ...Option) (*Service, error) { // Load all available languages langs := loader.Languages() if len(langs) == 0 { - return nil, fmt.Errorf("no languages available from loader") + return nil, errors.New("no languages available from loader") } for _, lang := range langs { @@ -223,7 +224,7 @@ func (s *Service) SetLanguage(lang string) error { } if len(s.availableLangs) == 0 { - return fmt.Errorf("no languages available") + return errors.New("no languages available") } matcher := language.NewMatcher(s.availableLangs) diff --git a/pkg/manifest/sign.go b/pkg/manifest/sign.go index c8699b9..857d15c 100644 --- a/pkg/manifest/sign.go +++ b/pkg/manifest/sign.go @@ -3,6 +3,7 @@ package manifest import ( "crypto/ed25519" "encoding/base64" + "errors" "fmt" "gopkg.in/yaml.v3" @@ -29,7 +30,7 @@ func Sign(m *Manifest, priv ed25519.PrivateKey) error { // Verify checks the ed25519 signature in m.Sign against the public key. func Verify(m *Manifest, pub ed25519.PublicKey) (bool, error) { if m.Sign == "" { - return false, fmt.Errorf("manifest.Verify: no signature present") + return false, errors.New("manifest.Verify: no signature present") } sig, err := base64.StdEncoding.DecodeString(m.Sign) if err != nil { diff --git a/pkg/process/runner.go b/pkg/process/runner.go index effd39a..8eb1fd5 100644 --- a/pkg/process/runner.go +++ b/pkg/process/runner.go @@ -2,7 +2,7 @@ package process import ( "context" - "fmt" + "errors" "sync" "time" ) @@ -104,7 +104,7 @@ func (r *Runner) RunAll(ctx context.Context, specs []RunSpec) (*RunAllResult, er Name: name, Spec: remaining[name], Skipped: true, - Error: fmt.Errorf("circular dependency or missing dependency"), + Error: errors.New("circular dependency or missing dependency"), }) } break @@ -136,7 +136,7 @@ func (r *Runner) RunAll(ctx context.Context, specs []RunSpec) (*RunAllResult, er Name: spec.Name, Spec: spec, Skipped: true, - Error: fmt.Errorf("skipped due to dependency failure"), + Error: errors.New("skipped due to dependency failure"), } } else { result = r.runSpec(ctx, spec) diff --git a/pkg/repos/registry.go b/pkg/repos/registry.go index 07c7486..3ee519d 100644 --- a/pkg/repos/registry.go +++ b/pkg/repos/registry.go @@ -4,6 +4,7 @@ package repos import ( + "errors" "fmt" "os" "path/filepath" @@ -146,7 +147,7 @@ func FindRegistry(m io.Medium) (string, error) { } } - return "", fmt.Errorf("repos.yaml not found") + return "", errors.New("repos.yaml not found") } // ScanDirectory creates a Registry by scanning a directory for git repos. diff --git a/pkg/session/video.go b/pkg/session/video.go index 2258fe1..d19893c 100644 --- a/pkg/session/video.go +++ b/pkg/session/video.go @@ -1,6 +1,7 @@ package session import ( + "errors" "fmt" "os" "os/exec" @@ -10,7 +11,7 @@ import ( // RenderMP4 generates an MP4 video from session events using VHS (charmbracelet). func RenderMP4(sess *Session, outputPath string) error { if _, err := exec.LookPath("vhs"); err != nil { - return fmt.Errorf("vhs not installed (go install github.com/charmbracelet/vhs@latest)") + return errors.New("vhs not installed (go install github.com/charmbracelet/vhs@latest)") } tape := generateTape(sess, outputPath) diff --git a/pkg/webview/actions.go b/pkg/webview/actions.go index 4dcc0ab..5348020 100644 --- a/pkg/webview/actions.go +++ b/pkg/webview/actions.go @@ -2,6 +2,7 @@ package webview import ( "context" + "errors" "fmt" "time" ) @@ -191,7 +192,7 @@ func (a HoverAction) Execute(ctx context.Context, wv *Webview) error { } if elem.BoundingBox == nil { - return fmt.Errorf("element has no bounding box") + return errors.New("element has no bounding box") } x := elem.BoundingBox.X + elem.BoundingBox.Width/2 @@ -495,7 +496,7 @@ func (wv *Webview) DragAndDrop(sourceSelector, targetSelector string) error { return fmt.Errorf("source element not found: %w", err) } if source.BoundingBox == nil { - return fmt.Errorf("source element has no bounding box") + return errors.New("source element has no bounding box") } target, err := wv.querySelector(ctx, targetSelector) @@ -503,7 +504,7 @@ func (wv *Webview) DragAndDrop(sourceSelector, targetSelector string) error { return fmt.Errorf("target element not found: %w", err) } if target.BoundingBox == nil { - return fmt.Errorf("target element has no bounding box") + return errors.New("target element has no bounding box") } // Calculate center points diff --git a/pkg/webview/angular.go b/pkg/webview/angular.go index 0a842c7..105d519 100644 --- a/pkg/webview/angular.go +++ b/pkg/webview/angular.go @@ -2,6 +2,7 @@ package webview import ( "context" + "errors" "fmt" "time" ) @@ -42,7 +43,7 @@ func (ah *AngularHelper) waitForAngular(ctx context.Context) error { return err } if !isAngular { - return fmt.Errorf("not an Angular application") + return errors.New("not an Angular application") } // Wait for Zone.js stability @@ -278,13 +279,13 @@ func (ah *AngularHelper) GetRouterState() (*AngularRouterState, error) { } if result == nil { - return nil, fmt.Errorf("could not get router state") + return nil, errors.New("could not get router state") } // Parse result resultMap, ok := result.(map[string]any) if !ok { - return nil, fmt.Errorf("invalid router state format") + return nil, errors.New("invalid router state format") } state := &AngularRouterState{ diff --git a/pkg/webview/cdp.go b/pkg/webview/cdp.go index f00d1f1..cea7f05 100644 --- a/pkg/webview/cdp.go +++ b/pkg/webview/cdp.go @@ -3,6 +3,7 @@ package webview import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -121,7 +122,7 @@ func NewCDPClient(debugURL string) (*CDPClient, error) { } if wsURL == "" { - return nil, fmt.Errorf("no WebSocket URL available") + return nil, errors.New("no WebSocket URL available") } // Connect to WebSocket @@ -306,7 +307,7 @@ func (c *CDPClient) NewTab(url string) (*CDPClient, error) { } if target.WebSocketDebuggerURL == "" { - return nil, fmt.Errorf("no WebSocket URL for new tab") + return nil, errors.New("no WebSocket URL for new tab") } // Connect to new tab diff --git a/pkg/webview/webview.go b/pkg/webview/webview.go index d18bf6e..d01fd50 100644 --- a/pkg/webview/webview.go +++ b/pkg/webview/webview.go @@ -24,6 +24,7 @@ package webview import ( "context" "encoding/base64" + "errors" "fmt" "sync" "time" @@ -122,7 +123,7 @@ func New(opts ...Option) (*Webview, error) { if wv.client == nil { cancel() - return nil, fmt.Errorf("no debug URL provided; use WithDebugURL option") + return nil, errors.New("no debug URL provided; use WithDebugURL option") } // Enable console capture @@ -222,7 +223,7 @@ func (wv *Webview) Screenshot() ([]byte, error) { dataStr, ok := result["data"].(string) if !ok { - return nil, fmt.Errorf("invalid screenshot data") + return nil, errors.New("invalid screenshot data") } data, err := base64.StdEncoding.DecodeString(dataStr) @@ -263,7 +264,7 @@ func (wv *Webview) GetURL() (string, error) { url, ok := result.(string) if !ok { - return "", fmt.Errorf("invalid URL result") + return "", errors.New("invalid URL result") } return url, nil @@ -281,7 +282,7 @@ func (wv *Webview) GetTitle() (string, error) { title, ok := result.(string) if !ok { - return "", fmt.Errorf("invalid title result") + return "", errors.New("invalid title result") } return title, nil @@ -306,7 +307,7 @@ func (wv *Webview) GetHTML(selector string) (string, error) { html, ok := result.(string) if !ok { - return "", fmt.Errorf("invalid HTML result") + return "", errors.New("invalid HTML result") } return html, nil @@ -520,7 +521,7 @@ func (wv *Webview) evaluate(ctx context.Context, script string) (any, error) { return nil, fmt.Errorf("JavaScript error: %s", description) } } - return nil, fmt.Errorf("JavaScript error") + return nil, errors.New("JavaScript error") } // Extract result value @@ -541,12 +542,12 @@ func (wv *Webview) querySelector(ctx context.Context, selector string) (*Element root, ok := docResult["root"].(map[string]any) if !ok { - return nil, fmt.Errorf("invalid document root") + return nil, errors.New("invalid document root") } rootID, ok := root["nodeId"].(float64) if !ok { - return nil, fmt.Errorf("invalid root node ID") + return nil, errors.New("invalid root node ID") } // Query selector @@ -576,12 +577,12 @@ func (wv *Webview) querySelectorAll(ctx context.Context, selector string) ([]*El root, ok := docResult["root"].(map[string]any) if !ok { - return nil, fmt.Errorf("invalid document root") + return nil, errors.New("invalid document root") } rootID, ok := root["nodeId"].(float64) if !ok { - return nil, fmt.Errorf("invalid root node ID") + return nil, errors.New("invalid root node ID") } // Query selector all @@ -595,7 +596,7 @@ func (wv *Webview) querySelectorAll(ctx context.Context, selector string) ([]*El nodeIDs, ok := queryResult["nodeIds"].([]any) if !ok { - return nil, fmt.Errorf("invalid node IDs") + return nil, errors.New("invalid node IDs") } elements := make([]*ElementInfo, 0, len(nodeIDs)) @@ -622,7 +623,7 @@ func (wv *Webview) getElementInfo(ctx context.Context, nodeID int) (*ElementInfo node, ok := descResult["node"].(map[string]any) if !ok { - return nil, fmt.Errorf("invalid node description") + return nil, errors.New("invalid node description") } tagName, _ := node["nodeName"].(string) diff --git a/pkg/ws/ws.go b/pkg/ws/ws.go index 16dd6f7..ea9cc03 100644 --- a/pkg/ws/ws.go +++ b/pkg/ws/ws.go @@ -47,6 +47,7 @@ package ws import ( "context" "encoding/json" + "errors" "fmt" "net/http" "sync" @@ -220,7 +221,7 @@ func (h *Hub) Broadcast(msg Message) error { select { case h.broadcast <- data: default: - return fmt.Errorf("broadcast channel full") + return errors.New("broadcast channel full") } return nil } From 13ed6d3f76c189d52e53be602f8db2b5d595f278 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:39:19 +0000 Subject: [PATCH 3/7] chore: use %w for error wrapping Co-Authored-By: Claude Opus 4.6 --- pkg/plugin/installer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugin/installer.go b/pkg/plugin/installer.go index 8854432..405b433 100644 --- a/pkg/plugin/installer.go +++ b/pkg/plugin/installer.go @@ -159,7 +159,7 @@ func (i *Installer) cloneRepo(ctx context.Context, org, repo, version, dest stri cmd := exec.CommandContext(ctx, "gh", args...) if output, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("%s: %s", err, strings.TrimSpace(string(output))) + return fmt.Errorf("%w: %s", err, strings.TrimSpace(string(output))) } return nil From ff530d989804cdbd45583bae0806d1376bcd7f45 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:45:48 +0000 Subject: [PATCH 4/7] =?UTF-8?q?chore:=20sort.Slice=20=E2=86=92=20slices.So?= =?UTF-8?q?rtFunc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- pkg/help/search.go | 19 ++++++++++--------- pkg/i18n/internal/validate/main.go | 11 ++++++----- pkg/io/datanode/client.go | 7 ++++--- pkg/io/node/node.go | 7 ++++--- pkg/lab/collector/influxdb.go | 10 +++++++--- pkg/lab/handler/chart.go | 10 ++++++---- pkg/lab/handler/web.go | 14 +++++++++----- pkg/plugin/registry.go | 7 ++++--- pkg/session/parser.go | 5 +++-- 9 files changed, 53 insertions(+), 37 deletions(-) diff --git a/pkg/help/search.go b/pkg/help/search.go index 8f1593c..c718a2b 100644 --- a/pkg/help/search.go +++ b/pkg/help/search.go @@ -1,8 +1,9 @@ package help import ( + "cmp" "regexp" - "sort" + "slices" "strings" "unicode" ) @@ -158,11 +159,11 @@ func (i *searchIndex) Search(query string) []*SearchResult { } // Sort by score (highest first) - sort.Slice(results, func(a, b int) bool { - if results[a].Score != results[b].Score { - return results[a].Score > results[b].Score + slices.SortFunc(results, func(a, b *SearchResult) int { + if a.Score != b.Score { + return cmp.Compare(b.Score, a.Score) // descending } - return results[a].Topic.Title < results[b].Topic.Title + return cmp.Compare(a.Topic.Title, b.Topic.Title) }) return results @@ -357,11 +358,11 @@ func highlight(text string, res []*regexp.Regexp) string { } // Sort matches by start position - sort.Slice(matches, func(i, j int) bool { - if matches[i].start != matches[j].start { - return matches[i].start < matches[j].start + slices.SortFunc(matches, func(a, b match) int { + if a.start != b.start { + return cmp.Compare(a.start, b.start) } - return matches[i].end > matches[j].end + return cmp.Compare(b.end, a.end) // descending }) // Merge overlapping or adjacent matches diff --git a/pkg/i18n/internal/validate/main.go b/pkg/i18n/internal/validate/main.go index d295c57..1887489 100644 --- a/pkg/i18n/internal/validate/main.go +++ b/pkg/i18n/internal/validate/main.go @@ -18,6 +18,7 @@ package main import ( + "cmp" "encoding/json" "errors" "fmt" @@ -26,7 +27,7 @@ import ( "go/token" "os" "path/filepath" - "sort" + "slices" "strings" ) @@ -508,11 +509,11 @@ func printReport(result ValidationResult) { fmt.Printf("-------------\n") // Sort by file then line - sort.Slice(result.MissingKeys, func(i, j int) bool { - if result.MissingKeys[i].File != result.MissingKeys[j].File { - return result.MissingKeys[i].File < result.MissingKeys[j].File + slices.SortFunc(result.MissingKeys, func(a, b KeyUsage) int { + if a.File != b.File { + return cmp.Compare(a.File, b.File) } - return result.MissingKeys[i].Line < result.MissingKeys[j].Line + return cmp.Compare(a.Line, b.Line) }) for _, usage := range result.MissingKeys { diff --git a/pkg/io/datanode/client.go b/pkg/io/datanode/client.go index 8f77041..f423b96 100644 --- a/pkg/io/datanode/client.go +++ b/pkg/io/datanode/client.go @@ -7,11 +7,12 @@ package datanode import ( + "cmp" goio "io" "io/fs" "os" "path" - "sort" + "slices" "strings" "sync" "time" @@ -359,8 +360,8 @@ func (m *Medium) List(p string) ([]fs.DirEntry, error) { } } - sort.Slice(entries, func(i, j int) bool { - return entries[i].Name() < entries[j].Name() + slices.SortFunc(entries, func(a, b fs.DirEntry) int { + return cmp.Compare(a.Name(), b.Name()) }) return entries, nil diff --git a/pkg/io/node/node.go b/pkg/io/node/node.go index 184ccc0..69c59bf 100644 --- a/pkg/io/node/node.go +++ b/pkg/io/node/node.go @@ -6,11 +6,12 @@ package node import ( "archive/tar" "bytes" + "cmp" goio "io" "io/fs" "os" "path" - "sort" + "slices" "strings" "time" @@ -335,8 +336,8 @@ func (n *Node) ReadDir(name string) ([]fs.DirEntry, error) { } } - sort.Slice(entries, func(i, j int) bool { - return entries[i].Name() < entries[j].Name() + slices.SortFunc(entries, func(a, b fs.DirEntry) int { + return cmp.Compare(a.Name(), b.Name()) }) return entries, nil diff --git a/pkg/lab/collector/influxdb.go b/pkg/lab/collector/influxdb.go index c5d79aa..950c80c 100644 --- a/pkg/lab/collector/influxdb.go +++ b/pkg/lab/collector/influxdb.go @@ -1,11 +1,12 @@ package collector import ( + "cmp" "context" "encoding/json" "fmt" "net/http" - "sort" + "slices" "strings" "time" @@ -126,8 +127,11 @@ func (i *InfluxDB) Collect(ctx context.Context) error { for _, r := range runSet { data.Runs = append(data.Runs, r) } - sort.Slice(data.Runs, func(i, j int) bool { - return data.Runs[i].Model < data.Runs[j].Model || (data.Runs[i].Model == data.Runs[j].Model && data.Runs[i].RunID < data.Runs[j].RunID) + slices.SortFunc(data.Runs, func(a, b lab.BenchmarkRun) int { + if c := cmp.Compare(a.Model, b.Model); c != 0 { + return c + } + return cmp.Compare(a.RunID, b.RunID) }) i.store.SetBenchmarks(data) diff --git a/pkg/lab/handler/chart.go b/pkg/lab/handler/chart.go index adcfc07..5e179ab 100644 --- a/pkg/lab/handler/chart.go +++ b/pkg/lab/handler/chart.go @@ -1,9 +1,11 @@ package handler import ( + "cmp" "fmt" "html/template" "math" + "slices" "sort" "strings" @@ -118,7 +120,7 @@ func LossChart(points []lab.LossPoint) template.HTML { // Draw train loss line (dimmed). if len(trainPts) > 1 { - sort.Slice(trainPts, func(i, j int) bool { return trainPts[i].Iteration < trainPts[j].Iteration }) + slices.SortFunc(trainPts, func(a, b lab.LossPoint) int { return cmp.Compare(a.Iteration, b.Iteration) }) sb.WriteString(` 0 { @@ -232,7 +234,7 @@ func ContentChart(points []lab.ContentPoint) template.HTML { if !ok || len(pts) < 2 { continue } - sort.Slice(pts, func(i, j int) bool { return pts[i].Iteration < pts[j].Iteration }) + slices.SortFunc(pts, func(a, b lab.ContentPoint) int { return cmp.Compare(a.Iteration, b.Iteration) }) // Average duplicate iterations. averaged := averageByIteration(pts) @@ -285,7 +287,7 @@ func CapabilityChart(points []lab.CapabilityPoint) template.HTML { overall = append(overall, p) } } - sort.Slice(overall, func(i, j int) bool { return overall[i].Iteration < overall[j].Iteration }) + slices.SortFunc(overall, func(a, b lab.CapabilityPoint) int { return cmp.Compare(a.Iteration, b.Iteration) }) if len(overall) == 0 { return template.HTML(`
No overall capability data
`) diff --git a/pkg/lab/handler/web.go b/pkg/lab/handler/web.go index ed3bfc4..146c560 100644 --- a/pkg/lab/handler/web.go +++ b/pkg/lab/handler/web.go @@ -1,11 +1,12 @@ package handler import ( + "cmp" "embed" "fmt" "html/template" "net/http" - "sort" + "slices" "strings" "time" @@ -376,11 +377,14 @@ func buildModelGroups(runs []lab.TrainingRunStatus, benchmarks lab.BenchmarkData } result = append(result, *g) } - sort.Slice(result, func(i, j int) bool { - if result[i].HasTraining != result[j].HasTraining { - return result[i].HasTraining + slices.SortFunc(result, func(a, b ModelGroup) int { + if a.HasTraining != b.HasTraining { + if a.HasTraining { + return -1 + } + return 1 } - return result[i].Model < result[j].Model + return cmp.Compare(a.Model, b.Model) }) return result } diff --git a/pkg/plugin/registry.go b/pkg/plugin/registry.go index b2f0a85..7685a68 100644 --- a/pkg/plugin/registry.go +++ b/pkg/plugin/registry.go @@ -1,9 +1,10 @@ package plugin import ( + "cmp" "encoding/json" "path/filepath" - "sort" + "slices" core "forge.lthn.ai/core/go/pkg/framework/core" "forge.lthn.ai/core/go/pkg/io" @@ -34,8 +35,8 @@ func (r *Registry) List() []*PluginConfig { for _, cfg := range r.plugins { result = append(result, cfg) } - sort.Slice(result, func(i, j int) bool { - return result[i].Name < result[j].Name + slices.SortFunc(result, func(a, b *PluginConfig) int { + return cmp.Compare(a.Name, b.Name) }) return result } diff --git a/pkg/session/parser.go b/pkg/session/parser.go index 4c3dd87..a66bae8 100644 --- a/pkg/session/parser.go +++ b/pkg/session/parser.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "sort" "strings" "time" @@ -156,8 +157,8 @@ func ListSessions(projectsDir string) ([]Session, error) { sessions = append(sessions, s) } - sort.Slice(sessions, func(i, j int) bool { - return sessions[i].StartTime.After(sessions[j].StartTime) + slices.SortFunc(sessions, func(a, b Session) int { + return b.StartTime.Compare(a.StartTime) // descending }) return sessions, nil From d60e87dac8904d8181500f8430790ca807189e25 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:52:06 +0000 Subject: [PATCH 5/7] chore: use min()/max() builtins (Go 1.21+) Co-Authored-By: Claude Opus 4.6 --- pkg/help/search.go | 19 +++--------- pkg/io/sigil/crypto_sigil.go | 10 ++---- pkg/lab/handler/chart.go | 59 ++++++++---------------------------- pkg/lab/handler/web.go | 5 +-- 4 files changed, 20 insertions(+), 73 deletions(-) diff --git a/pkg/help/search.go b/pkg/help/search.go index c718a2b..bb2a24b 100644 --- a/pkg/help/search.go +++ b/pkg/help/search.go @@ -286,24 +286,15 @@ func extractSnippet(content string, res []*regexp.Regexp) string { if matchPos == -1 { // No match found, use start of content start = 0 - end = snippetLen - if end > runeLen { - end = runeLen - } + end = min(snippetLen, runeLen) } else { // Convert byte position to rune position matchRunePos := len([]rune(content[:matchPos])) // Extract snippet around match (rune-based) - start = matchRunePos - 50 - if start < 0 { - start = 0 - } + start = max(matchRunePos-50, 0) - end = start + snippetLen - if end > runeLen { - end = runeLen - } + end = min(start+snippetLen, runeLen) } snippet := string(runes[start:end]) @@ -371,9 +362,7 @@ func highlight(text string, res []*regexp.Regexp) string { curr := matches[0] for i := 1; i < len(matches); i++ { if matches[i].start <= curr.end { - if matches[i].end > curr.end { - curr.end = matches[i].end - } + curr.end = max(curr.end, matches[i].end) } else { merged = append(merged, curr) curr = matches[i] diff --git a/pkg/io/sigil/crypto_sigil.go b/pkg/io/sigil/crypto_sigil.go index 98c25cc..8bacd44 100644 --- a/pkg/io/sigil/crypto_sigil.go +++ b/pkg/io/sigil/crypto_sigil.go @@ -103,10 +103,7 @@ func (x *XORObfuscator) deriveKeyStream(entropy []byte, length int) []byte { h.Write(blockBytes[:]) block := h.Sum(nil) - copyLen := len(block) - if offset+copyLen > length { - copyLen = length - offset - } + copyLen := min(len(block), length-offset) copy(stream[offset:], block[:copyLen]) offset += copyLen blockNum++ @@ -222,10 +219,7 @@ func (s *ShuffleMaskObfuscator) deriveMask(entropy []byte, length int) []byte { h.Write(blockBytes[:]) block := h.Sum(nil) - copyLen := len(block) - if offset+copyLen > length { - copyLen = length - offset - } + copyLen := min(len(block), length-offset) copy(mask[offset:], block[:copyLen]) offset += copyLen blockNum++ diff --git a/pkg/lab/handler/chart.go b/pkg/lab/handler/chart.go index 5e179ab..abda97f 100644 --- a/pkg/lab/handler/chart.go +++ b/pkg/lab/handler/chart.go @@ -62,25 +62,15 @@ func LossChart(points []lab.LossPoint) template.HTML { yMin, yMax := allPts[0].Loss, allPts[0].Loss for _, p := range allPts { x := float64(p.Iteration) - if x < xMin { - xMin = x - } - if x > xMax { - xMax = x - } - if p.Loss < yMin { - yMin = p.Loss - } - if p.Loss > yMax { - yMax = p.Loss - } + xMin = min(xMin, x) + xMax = max(xMax, x) + yMin = min(yMin, p.Loss) + yMax = max(yMax, p.Loss) } // Add padding to Y range. yRange := yMax - yMin - if yRange < 0.1 { - yRange = 0.1 - } + yRange = max(yRange, 0.1) yMin = yMin - yRange*0.1 yMax = yMax + yRange*0.1 if xMax == xMin { @@ -104,13 +94,7 @@ func LossChart(points []lab.LossPoint) template.HTML { } // X axis labels. - nGridX := 6 - if int(xMax-xMin) < nGridX { - nGridX = int(xMax - xMin) - } - if nGridX < 1 { - nGridX = 1 - } + nGridX := max(min(6, int(xMax-xMin)), 1) for i := 0; i <= nGridX; i++ { xVal := xMin + float64(i)*(xMax-xMin)/float64(nGridX) x := scaleX(xVal) @@ -534,21 +518,14 @@ func DomainChart(stats []lab.DomainStat) template.HTML { if len(stats) == 0 { return "" } - limit := 25 - if len(stats) < limit { - limit = len(stats) - } + limit := min(25, len(stats)) items := stats[:limit] maxCount := 0 for _, d := range items { - if d.Count > maxCount { - maxCount = d.Count - } - } - if maxCount == 0 { - maxCount = 1 + maxCount = max(maxCount, d.Count) } + maxCount = max(maxCount, 1) barH := 18 gap := 4 @@ -563,10 +540,7 @@ func DomainChart(stats []lab.DomainStat) template.HTML { for i, d := range items { y := i*(barH+gap) + 5 - barW := int(float64(d.Count) / float64(maxCount) * float64(barAreaW)) - if barW < 2 { - barW = 2 - } + barW := max(int(float64(d.Count)/float64(maxCount)*float64(barAreaW)), 2) fmt.Fprintf(&b, `%s`, labelW-8, y+barH/2, template.HTMLEscapeString(d.Domain)) fmt.Fprintf(&b, ``, @@ -587,13 +561,9 @@ func VoiceChart(stats []lab.VoiceStat) template.HTML { maxCount := 0 for _, v := range stats { - if v.Count > maxCount { - maxCount = v.Count - } - } - if maxCount == 0 { - maxCount = 1 + maxCount = max(maxCount, v.Count) } + maxCount = max(maxCount, 1) barW := 50 gap := 8 @@ -609,10 +579,7 @@ func VoiceChart(stats []lab.VoiceStat) template.HTML { for i, v := range stats { x := i*(barW+gap) + gap + 5 - barH := int(float64(v.Count) / float64(maxCount) * float64(chartHeight)) - if barH < 2 { - barH = 2 - } + barH := max(int(float64(v.Count)/float64(maxCount)*float64(chartHeight)), 2) y := topPad + chartHeight - barH fmt.Fprintf(&b, ``, diff --git a/pkg/lab/handler/web.go b/pkg/lab/handler/web.go index 146c560..b4b9d3f 100644 --- a/pkg/lab/handler/web.go +++ b/pkg/lab/handler/web.go @@ -73,10 +73,7 @@ func NewWebHandler(s *lab.Store) *WebHandler { if cores <= 0 { return "0" } - pct := load / float64(cores) * 100 - if pct > 100 { - pct = 100 - } + pct := min(load/float64(cores)*100, 100) return fmt.Sprintf("%.0f", pct) }, "fmtGB": func(v float64) string { From eb186027a0f2688b3a7b671590c910b99d0a73da Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:53:09 +0000 Subject: [PATCH 6/7] chore: use range-over-integer (Go 1.22+) Co-Authored-By: Claude Opus 4.6 --- pkg/webview/actions.go | 2 +- pkg/ws/ws.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/webview/actions.go b/pkg/webview/actions.go index 5348020..303ba9d 100644 --- a/pkg/webview/actions.go +++ b/pkg/webview/actions.go @@ -235,7 +235,7 @@ func (a DoubleClickAction) Execute(ctx context.Context, wv *Webview) error { y := elem.BoundingBox.Y + elem.BoundingBox.Height/2 // Double click sequence - for i := 0; i < 2; i++ { + for i := range 2 { for _, eventType := range []string{"mousePressed", "mouseReleased"} { _, err := wv.client.Call(ctx, "Input.dispatchMouseEvent", map[string]any{ "type": eventType, diff --git a/pkg/ws/ws.go b/pkg/ws/ws.go index ea9cc03..f836952 100644 --- a/pkg/ws/ws.go +++ b/pkg/ws/ws.go @@ -425,7 +425,7 @@ func (c *Client) writePump() { // Batch queued messages n := len(c.send) - for i := 0; i < n; i++ { + for range n { w.Write([]byte{'\n'}) w.Write(<-c.send) } From 2b09a265074d309939d49f2e196b8e66a5f9a7dc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 15:54:39 +0000 Subject: [PATCH 7/7] chore: use slices.Contains for linear search Co-Authored-By: Claude Opus 4.6 --- pkg/coredeno/permissions.go | 15 +++------------ pkg/framework/core/interfaces.go | 8 ++------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/pkg/coredeno/permissions.go b/pkg/coredeno/permissions.go index f80e0d8..86a0671 100644 --- a/pkg/coredeno/permissions.go +++ b/pkg/coredeno/permissions.go @@ -2,6 +2,7 @@ package coredeno import ( "path/filepath" + "slices" "strings" ) @@ -25,20 +26,10 @@ func CheckPath(path string, allowed []string) bool { // CheckNet returns true if the given host:port is in the allowed list. func CheckNet(addr string, allowed []string) bool { - for _, a := range allowed { - if a == addr { - return true - } - } - return false + return slices.Contains(allowed, addr) } // CheckRun returns true if the given command is in the allowed list. func CheckRun(cmd string, allowed []string) bool { - for _, a := range allowed { - if a == cmd { - return true - } - } - return false + return slices.Contains(allowed, cmd) } diff --git a/pkg/framework/core/interfaces.go b/pkg/framework/core/interfaces.go index a347ebb..ee74b47 100644 --- a/pkg/framework/core/interfaces.go +++ b/pkg/framework/core/interfaces.go @@ -4,6 +4,7 @@ import ( "context" "embed" goio "io" + "slices" "sync/atomic" ) @@ -29,12 +30,7 @@ type Features struct { // IsEnabled returns true if the given feature is enabled. func (f *Features) IsEnabled(feature string) bool { - for _, flag := range f.Flags { - if flag == feature { - return true - } - } - return false + return slices.Contains(f.Flags, feature) } // Option is a function that configures the Core.