refactor(go): adopt modern stdlib idioms and gate them (#7831)

* refactor(go): adopt modern stdlib idioms across kernel and desktop

Mechanical sweep with golangci-lint's modernize/intrange/copyloopvar/
usestdlibvars fixers plus hand-fixes for what they could not rewrite:
slices.Contains, min/max, slices.Backward, WaitGroup.Go, reflect.TypeFor,
maps.Copy, strings.Cut/CutPrefix/SplitSeq, strings.Builder, range-over-int.
Net -549 lines with no behavior change.

Three loops in session.go stay index-based: slices.Backward yields element
copies, so the fixer's rewrite silently dropped writes to DecisionReceipts
and ToolCalls (caught by TestDelivery* and TestDependentSameBatch*). All 49
slices.Backward sites were audited for that shape; only these three write
through the index, and they carry a nolint with the reason.

CaptureAfter's scan loop had an empty body and never ran any logic, so the
fixer could not express it. Drop it and the accessor it was the only caller
of. omitzero is deliberately untouched: turning omitempty into omitzero
changes the JSON written for nested structs, which is a wire-format change
to session files and the extension protocol.

* build(lint): gate the modern-idiom linters

Nothing stopped the codebase from drifting back to pre-1.22 idioms. Both
modules are now clean for modernize, intrange, copyloopvar, and
usestdlibvars, so enable them rather than leaving the sweep as a one-off.

omitzero stays disabled with the reason inline; usetesting, perfsprint, and
errorlint still have residue and land separately.
This commit is contained in:
YHH authored and GitHub committed 2026-08-07 15:30:33 +08:00
1 parent e1079f2b58
commit 4fbbca6a99
428 files changed
+1558 -2248

No files matched your search

+10
View File
@@ -2,13 +2,23 @@ version: "2"
linters:
enable:
- copyloopvar
- errcheck
- errorlint
- govet
- ineffassign
- intrange
- modernize
- staticcheck
- unused
- usestdlibvars
settings:
modernize:
# omitempty -> omitzero changes which fields are written for nested
# structs. That is a wire-format change to session files and the
# extension protocol, not a refactor, so it lands on its own.
disable:
- omitzero
errcheck:
check-type-assertions: false
exclude-functions:
+3 -3
View File
@@ -55,7 +55,7 @@ func fakeGoFile(nonce string, i, size int) string {
func seedSession(nonce string) *agent.Session {
s := agent.NewSession("You are a terse coding agent reviewing a Go codebase.")
s.Add(provider.Message{Role: provider.RoleUser, Content: "Review every file in module " + nonce + " one by one. Keep notes short."})
for i := 0; i < fatResults; i++ {
for i := range fatResults {
id := fmt.Sprintf("c%02d", i)
name := fmt.Sprintf("src/file%02d.go", i)
s.Add(provider.Message{Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: id, Name: "read_file", Arguments: fmt.Sprintf(`{"path":%q}`, name)}}})
@@ -180,7 +180,7 @@ func resume(dir string) {
func comprehension(trials int) {
p := prov()
pass := 0
for t := 0; t < trials; t++ {
for t := range trials {
dir, err := os.MkdirTemp("", "cm-e2e-")
if err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -198,7 +198,7 @@ func comprehension(trials int) {
s.Add(provider.Message{Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: "r1", Name: "read_file", Arguments: `{"path":"config.go"}`}}})
s.Add(provider.Message{Role: provider.RoleTool, ToolCallID: "r1", Name: "read_file", Content: content})
s.Add(provider.Message{Role: provider.RoleAssistant, Content: "Noted the constants in config.go."})
for i := 0; i < 4; i++ {
for i := range 4 {
s.Add(provider.Message{Role: provider.RoleUser, Content: fmt.Sprintf("ack %d", i)})
s.Add(provider.Message{Role: provider.RoleAssistant, Content: "ok"})
}
+17 -20
View File
@@ -48,10 +48,7 @@ func runDiff(o diffOpts) string {
pkgs := packagesOf(srcFiles)
prompt := buildDiffPrompt(srcFiles, pkgs, truncate(gitOut(o.repo, "diff", o.base+"...HEAD", "--")))
attempts := o.attempts
if attempts < 1 {
attempts = 1
}
attempts := max(o.attempts, 1)
var best diffReport
made := 0
for i := 1; i <= attempts; i++ {
@@ -406,7 +403,7 @@ func parseCoverProfile(repo, path string) map[string][]coverBlock {
return nil
}
out := map[string][]coverBlock{}
for _, ln := range strings.Split(string(data), "\n") {
for ln := range strings.SplitSeq(string(data), "\n") {
if ln == "" || strings.HasPrefix(ln, "mode:") {
continue
}
@@ -433,8 +430,8 @@ func repoRelFromModulePath(p string) string {
if strings.HasPrefix(p, prefix) {
return p[len(prefix):]
}
if i := strings.IndexByte(p, '/'); i >= 0 {
return p[i+1:]
if _, after, ok := strings.Cut(p, "/"); ok {
return after
}
return p
}
@@ -447,7 +444,7 @@ func changedLineSet(repo, base string, srcFiles []string) map[string]map[int]boo
out := map[string]map[int]bool{}
file := ""
newLine := 0
for _, ln := range strings.Split(diff, "\n") {
for ln := range strings.SplitSeq(diff, "\n") {
// '-' (deletion) lines are intentionally unhandled: they don't advance the
// new-side line counter, so they fall through with no case.
switch {
@@ -457,10 +454,10 @@ func changedLineSet(repo, base string, srcFiles []string) map[string]map[int]boo
case strings.HasPrefix(ln, "@@"):
// @@ -a,b +c,d @@ — start collecting at new-side line c.
// Digit-only cut: malformed headers (e.g. `@@ +abc @@`) fail closed.
if plus := strings.Index(ln, "+"); plus >= 0 {
num := ln[plus+1:]
if _, after, ok := strings.Cut(ln, "+"); ok {
num := after
end := len(num)
for i := 0; i < len(num); i++ {
for i := range len(num) {
if num[i] < '0' || num[i] > '9' {
end = i
break
@@ -503,9 +500,9 @@ func countAssertionPins(ps []pinResult) int {
func parseNewTests(diff string) []testRef {
var refs []testRef
pkg := ""
for _, ln := range strings.Split(diff, "\n") {
if strings.HasPrefix(ln, "+++ b/") {
pkg = "./" + filepath.ToSlash(filepath.Dir(strings.TrimPrefix(ln, "+++ b/")))
for ln := range strings.SplitSeq(diff, "\n") {
if after, ok := strings.CutPrefix(ln, "+++ b/"); ok {
pkg = "./" + filepath.ToSlash(filepath.Dir(after))
continue
}
if !strings.HasPrefix(ln, "+") || strings.HasPrefix(ln, "+++") {
@@ -519,11 +516,11 @@ func parseNewTests(diff string) []testRef {
// Method form `(r T) Name(...)` starts with '('; parse the receiver out before the name.
var name string
if sig[0] == '(' {
close := strings.IndexByte(sig, ')')
if close < 0 {
_, after, ok := strings.Cut(sig, ")")
if !ok {
continue
}
rest := strings.TrimSpace(sig[close+1:])
rest := strings.TrimSpace(after)
methodParen := strings.IndexByte(rest, '(')
if methodParen <= 0 {
continue
@@ -545,7 +542,7 @@ func parseNewTests(diff string) []testRef {
func countAdded(diff string) int {
n := 0
for _, ln := range strings.Split(diff, "\n") {
for ln := range strings.SplitSeq(diff, "\n") {
if strings.HasPrefix(ln, "+") && !strings.HasPrefix(ln, "+++") {
n++
}
@@ -557,7 +554,7 @@ func countAdded(diff string) int {
func failingTestNames(out string) []string {
var names []string
seen := map[string]bool{}
for _, ln := range strings.Split(out, "\n") {
for ln := range strings.SplitSeq(out, "\n") {
ln = strings.TrimSpace(ln)
if !strings.HasPrefix(ln, "--- FAIL:") {
continue
@@ -603,7 +600,7 @@ func changedGoFilesWorktree(repo string, includeTests bool) []string {
func filterGo(out string, includeTests bool) []string {
var keep []string
for _, f := range strings.Fields(strings.ReplaceAll(out, "\n", " ")) {
for f := range strings.FieldsSeq(strings.ReplaceAll(out, "\n", " ")) {
if strings.HasSuffix(f, "_test.go") && !includeTests {
continue
}
+2 -1
View File
@@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"sort"
"strings"
"time"
@@ -526,7 +527,7 @@ func median(ms []int64) int64 {
return 0
}
sorted := append([]int64(nil), ms...)
sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
slices.Sort(sorted)
return sorted[len(sorted)/2]
}
+1 -1
View File
@@ -129,7 +129,7 @@ func mutantBody(fset *token.FileSet, results *ast.FieldList) string {
if n == 0 {
n = 1
}
for i := 0; i < n; i++ {
for range n {
rets = append(rets, t)
}
}
+11 -20
View File
@@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"slices"
"strings"
"reasonix/internal/ablation"
@@ -137,30 +138,20 @@ func swebenchReportPath(model, runID string) string {
// taxonomy. An id the grader never mentions is reported as unknown rather than
// silently counted as unsolved.
func (r swebenchReport) gradedClass(instanceID string) string {
for _, id := range r.ResolvedIDs {
if id == instanceID {
return "solved"
}
if slices.Contains(r.ResolvedIDs, instanceID) {
return "solved"
}
for _, id := range r.EmptyPatchIDs {
if id == instanceID {
return "no_patch"
}
if slices.Contains(r.EmptyPatchIDs, instanceID) {
return "no_patch"
}
for _, id := range r.ErrorIDs {
if id == instanceID {
return "grader_error"
}
if slices.Contains(r.ErrorIDs, instanceID) {
return "grader_error"
}
for _, id := range r.IncompleteIDs {
if id == instanceID {
return "eval_timeout"
}
if slices.Contains(r.IncompleteIDs, instanceID) {
return "eval_timeout"
}
for _, id := range r.UnresolvedIDs {
if id == instanceID {
return "wrong_patch"
}
if slices.Contains(r.UnresolvedIDs, instanceID) {
return "wrong_patch"
}
return "ungraded"
}
+2 -2
View File
@@ -296,8 +296,8 @@ func proxyEnv(url string) []string {
}
func firstLine(s string) string {
if i := strings.IndexByte(s, '\n'); i >= 0 {
return strings.TrimSpace(s[:i])
if before, _, ok := strings.Cut(s, "\n"); ok {
return strings.TrimSpace(before)
}
return strings.TrimSpace(s)
}
+12 -19
View File
@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"log/slog"
"maps"
"mime"
"net/http"
"net/url"
@@ -20,6 +21,7 @@ import (
"path/filepath"
"regexp"
goruntime "runtime"
"slices"
"sort"
"strconv"
"strings"
@@ -2635,6 +2637,7 @@ func (a *App) CheckpointsForTab(tabID string) []CheckpointMeta {
canCodeAfter := true
codeFileSet := make(map[string]bool, len(metas)*2)
codeFilePreview := []string{}
//nolint:modernize // slices.Backward yields element copies; this body writes through the index.
for i := len(out) - 1; i >= 0; i-- {
if len(out[i].Files) > 0 {
hasCodeAfter = true
@@ -5681,10 +5684,7 @@ func historyPageFromMessages(messages []HistoryMessage, beforeTurn, limit int) H
if beforeTurn <= 0 || beforeTurn > totalTurns {
beforeTurn = totalTurns
}
startTurn := beforeTurn - limit
if startTurn < 0 {
startTurn = 0
}
startTurn := max(beforeTurn-limit, 0)
page := HistoryPage{
StartTurn: startTurn,
EndTurn: beforeTurn,
@@ -6033,10 +6033,7 @@ func historyPageFromProviderMessages(
if beforeTurn <= 0 || beforeTurn > totalTurns {
beforeTurn = totalTurns
}
startTurn := beforeTurn - limit
if startTurn < 0 {
startTurn = 0
}
startTurn := max(beforeTurn-limit, 0)
page := HistoryPage{
StartTurn: startTurn,
EndTurn: beforeTurn,
@@ -6323,7 +6320,7 @@ func historyLineCount(s string) int {
func historyNonEmptyLineCount(s string) int {
count := 0
for _, line := range strings.Split(s, "\n") {
for line := range strings.SplitSeq(s, "\n") {
if strings.TrimSpace(line) != "" {
count++
}
@@ -6622,9 +6619,9 @@ func updateHistoryToolCallSummary(out []HistoryMessage, callID, output string) {
if callID == "" {
return
}
for i := len(out) - 1; i >= 0; i-- {
for j := range out[i].ToolCalls {
call := &out[i].ToolCalls[j]
for _, v := range slices.Backward(out) {
for j := range v.ToolCalls {
call := &v.ToolCalls[j]
if call.ID != callID {
continue
}
@@ -8160,9 +8157,7 @@ func (a *App) mcpServersView() []ServerView {
}
ctrl := tab.Ctrl
disabled := make(map[string]ServerView, len(tab.disabledMCP))
for name, s := range tab.disabledMCP {
disabled[name] = s
}
maps.Copy(disabled, tab.disabledMCP)
order := append([]string(nil), tab.mcpOrder...)
workspaceRoot := tab.WorkspaceRoot
tabID := tab.ID
@@ -9447,9 +9442,7 @@ func cloneStringIntMap(values map[string]int) map[string]int {
return nil
}
out := make(map[string]int, len(values))
for key, value := range values {
out[key] = value
}
maps.Copy(out, values)
return out
}
@@ -11327,7 +11320,7 @@ func saveExclusiveExportPayloads(targets []string, payloadCount int, payloadAt f
// while bytes are staged; the caller restores finalMode only after the payload
// has been completely written and synced.
func createExportTempFile(dir string) (*os.File, os.FileMode, error) {
for attempt := 0; attempt < exportTempCreateAttempts; attempt++ {
for range exportTempCreateAttempts {
var suffix [12]byte
if _, err := rand.Read(suffix[:]); err != nil {
return nil, 0, fmt.Errorf("generate export temp name: %w", err)
+5 -7
View File
@@ -124,12 +124,10 @@ func TestScheduleSnapshotCoalesces(t *testing.T) {
_ = a
var wg sync.WaitGroup
for i := 0; i < 64; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 64 {
wg.Go(func() {
tab.sink.Emit(event.Event{Kind: event.TurnDone})
}()
})
}
wg.Wait()
@@ -335,11 +333,11 @@ func TestDesktopSnapshotConflictRecoveryRequiresRecoveryLease(t *testing.T) {
detachedSessions: map[string]*WorkspaceTab{},
activeTabID: "recovery_tab",
}
app.runtimeEvents.emit = func(ctx context.Context, name string, payload ...interface{}) {
app.runtimeEvents.emit = func(ctx context.Context, name string, payload ...any) {
runtimeEvents <- runtimeEventEnvelope{
ctx: ctx,
name: name,
payload: append([]interface{}(nil), payload...),
payload: append([]any(nil), payload...),
}
}
tab := &WorkspaceTab{
+1 -1
View File
@@ -35,7 +35,7 @@ func TestCarriedRebuildsKeepOneSession(t *testing.T) {
t.Fatal(err)
}
for i := 0; i < 5; i++ {
for range 5 {
prevPath := ctrl.SessionPath()
carried := ctrl.History()
ctrl.Close()
+36 -44
View File
@@ -13,6 +13,7 @@ import (
"os/exec"
"path/filepath"
"reflect"
"slices"
"strconv"
"strings"
"sync"
@@ -1058,14 +1059,14 @@ func TestBackgroundRestorePlanAvoidsNormalWindowFlash(t *testing.T) {
func TestEmitReadyInvokesReadyHook(t *testing.T) {
app := NewApp()
var calls int32
var calls atomic.Int32
app.readyHook = func() {
atomic.AddInt32(&calls, 1)
calls.Add(1)
}
app.emitReady(context.TODO())
if got := atomic.LoadInt32(&calls); got != 1 {
if got := calls.Load(); got != 1 {
t.Fatalf("ready hook calls = %d, want 1", got)
}
}
@@ -1227,7 +1228,7 @@ func BenchmarkDesktopSettingsPayloads(b *testing.B) {
b.Setenv("SHARED_PROVIDER_KEY", "sk-test")
cfg := config.LoadForEdit(config.UserConfigPath())
for i := 0; i < 40; i++ {
for i := range 40 {
cfg.Providers = append(cfg.Providers, config.ProviderEntry{
Name: fmt.Sprintf("custom-%02d", i),
Kind: "openai",
@@ -1243,12 +1244,12 @@ func BenchmarkDesktopSettingsPayloads(b *testing.B) {
app := NewApp()
b.Run("Settings", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for range b.N {
_ = app.Settings()
}
})
b.Run("DesktopStartupSettings", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for range b.N {
_ = app.DesktopStartupSettings()
}
})
@@ -2073,7 +2074,6 @@ func TestResetProviderPresetAccessRejectsMissingSameNameProvider(t *testing.T) {
func TestAddEveryProviderPresetAccessInstallsTemplate(t *testing.T) {
for _, preset := range config.CuratedProviderPresets() {
preset := preset
t.Run(preset.ID, func(t *testing.T) {
isolateDesktopUserDirs(t)
@@ -2890,7 +2890,7 @@ func TestSetEffortForTabReanchorsDepthCapRecoveryBranch(t *testing.T) {
app := NewApp()
app.ctx = context.Background()
app.runtimeEvents.emit = func(context.Context, string, ...interface{}) {}
app.runtimeEvents.emit = func(context.Context, string, ...any) {}
tab := &WorkspaceTab{
ID: "tab_depth_cap_effort",
Scope: "global",
@@ -3566,7 +3566,7 @@ func TestSetModelForTabContinuesRecoveryPathAfterSnapshotConflict(t *testing.T)
app := NewApp()
app.ctx = context.Background()
app.runtimeEvents.emit = func(context.Context, string, ...interface{}) {}
app.runtimeEvents.emit = func(context.Context, string, ...any) {}
tab := &WorkspaceTab{
ID: "tab_recovery_model",
Scope: "global",
@@ -4339,16 +4339,13 @@ func runQuickClickWorkspaceReconcileTest(t *testing.T, layoutStyle string) {
errs := make(chan error, len(actions))
var wg sync.WaitGroup
for _, action := range actions {
action := action
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
ready <- struct{}{}
<-start
if err := action.run(); err != nil {
errs <- fmt.Errorf("%s: %w", action.name, err)
}
}()
})
}
for range actions {
<-ready
@@ -6793,7 +6790,7 @@ func assertSingleTeardownTimeoutNotice(t *testing.T, notices <-chan event.Event,
t.Fatal("missing background-job teardown timeout notice")
}
var waited time.Duration
for _, field := range strings.Fields(notice.Detail) {
for field := range strings.FieldsSeq(notice.Detail) {
if !strings.HasPrefix(field, "waited=") {
continue
}
@@ -7297,7 +7294,7 @@ func BenchmarkDesktopListSessionsScoped(b *testing.B) {
if err := os.MkdirAll(dir, 0o755); err != nil {
b.Fatalf("mkdir %s: %v", dir, err)
}
for i := 0; i < 120; i++ {
for i := range 120 {
path := filepath.Join(dir, fmt.Sprintf("session-%03d.jsonl", i))
body := fmt.Sprintf(`{"role":"user","content":"session %03d"}`+"\n", i)
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
@@ -7312,7 +7309,7 @@ func BenchmarkDesktopListSessionsScoped(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
sessions := app.ListSessions()
if len(sessions) != 120 {
b.Fatalf("ListSessions len = %d, want 120", len(sessions))
@@ -7563,12 +7560,12 @@ func TestDesktopSharedHostProjectMCPConnectsWithoutLaunchApproval(t *testing.T)
srv := desktopMCPHTTPServer(t)
defer srv.Close()
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), []byte(fmt.Sprintf(`
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), fmt.Appendf(nil, `
[[plugins]]
name = "h"
type = "http"
url = %q
`, srv.URL)), 0o644); err != nil {
`, srv.URL), 0o644); err != nil {
t.Fatal(err)
}
@@ -7759,12 +7756,12 @@ func TestSetMCPServerEnabledSharedHostPreservesSiblingTabs(t *testing.T) {
srv := desktopMCPHTTPServer(t)
defer srv.Close()
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), []byte(fmt.Sprintf(`
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), fmt.Appendf(nil, `
[[plugins]]
name = "h"
type = "http"
url = %q
`, srv.URL)), 0o644); err != nil {
`, srv.URL), 0o644); err != nil {
t.Fatal(err)
}
@@ -7909,12 +7906,12 @@ func TestReconnectMCPServerUsesEffectiveProjectConfigWhenUserNameIsShadowed(t *t
if err := userCfg.SaveTo(config.UserConfigPath()); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), []byte(fmt.Sprintf(`
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), fmt.Appendf(nil, `
[[plugins]]
name = "h"
type = "http"
url = %q
`, projectServer.URL)), 0o644); err != nil {
`, projectServer.URL), 0o644); err != nil {
t.Fatal(err)
}
@@ -8055,7 +8052,7 @@ func newGatedDesktopMCPLaunchFixture(t *testing.T, startGateAddr string) gatedDe
gateConfig = fmt.Sprintf("DESKTOP_MCP_START_GATE_ADDR = %q\n", startGateAddr)
}
helperArgs := []string{"-test.run=TestDesktopMCPHelperProcess", "--"}
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), []byte(fmt.Sprintf(`
if err := os.WriteFile(filepath.Join(dir, "reasonix.toml"), fmt.Appendf(nil, `
[[plugins]]
name = "h"
command = %q
@@ -8067,7 +8064,7 @@ DESKTOP_MCP_SINGLE_INSTANCE_ADDR = %q
%s
[sandbox]
network = true
`, exe, singleInstanceAddr, gateConfig)), 0o644); err != nil {
`, exe, singleInstanceAddr, gateConfig), 0o644); err != nil {
t.Fatal(err)
}
@@ -8305,13 +8302,13 @@ func installGatedTestPluginPackage(t *testing.T, mcpServerName string) string {
if err := os.MkdirAll(root, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, pluginpkg.NativeManifest), []byte(fmt.Sprintf(`{
if err := os.WriteFile(filepath.Join(root, pluginpkg.NativeManifest), fmt.Appendf(nil, `{
"name": "review-helper",
"version": "1.0.0",
"mcpServers": {
%q: { "type": "stdio", "command": "helper" }
}
}`, mcpServerName)), 0o644); err != nil {
}`, mcpServerName), 0o644); err != nil {
t.Fatal(err)
}
if err := pluginpkg.Upsert(reasonixHome, pluginpkg.InstalledPlugin{
@@ -9082,13 +9079,13 @@ func TestRemoveMCPServerRejectsPluginManagedServerWithoutDisconnecting(t *testin
if err := os.MkdirAll(root, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, pluginpkg.NativeManifest), []byte(fmt.Sprintf(`{
if err := os.WriteFile(filepath.Join(root, pluginpkg.NativeManifest), fmt.Appendf(nil, `{
"name": "superpowers",
"version": "1.0.0",
"mcpServers": {
"helper": { "type": "http", "url": %q }
}
}`, srv.URL)), 0o644); err != nil {
}`, srv.URL), 0o644); err != nil {
t.Fatal(err)
}
if err := pluginpkg.Upsert(reasonixHome, pluginpkg.InstalledPlugin{
@@ -10127,7 +10124,7 @@ func TestRunShellForTabStaysBoundDuringRapidProjectTabSwitching(t *testing.T) {
waitForShellDispatch(t, shellEvents, marker)
waitForFile(t, filepath.Join(projectA, marker), "shell")
for i := 0; i < 8; i++ {
for range 8 {
if err := app.SetActiveTab("project-b"); err != nil {
t.Fatalf("SetActiveTab(project-b): %v", err)
}
@@ -10281,12 +10278,7 @@ func newBackgroundJobController(t *testing.T, label string) *control.Controller
}
func hasLevel(levels []string, want string) bool {
for _, level := range levels {
if level == want {
return true
}
}
return false
return slices.Contains(levels, want)
}
func hasCommand(cmds []CommandInfo, name string) bool {
@@ -10616,14 +10608,14 @@ func TestScanPromptHistoryFromDirUsesSessionActivityBeforeEventInterleaving(t *t
early := filepath.Join(dir, "early.jsonl")
late := filepath.Join(dir, "late.jsonl")
if err := os.WriteFile(early, []byte(fmt.Sprintf(`{"role":"user","content":"early first","time":%d}
if err := os.WriteFile(early, fmt.Appendf(nil, `{"role":"user","content":"early first","time":%d}
{"role":"assistant","content":"ok"}
{"role":"user","content":"early second","time":%d}
`, base.UnixMilli(), base.Add(time.Minute).UnixMilli())), 0o644); err != nil {
`, base.UnixMilli(), base.Add(time.Minute).UnixMilli()), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(late, []byte(fmt.Sprintf(`{"role":"user","content":"late newest","time":%d}
`, base.Add(2*time.Minute).UnixMilli())), 0o644); err != nil {
if err := os.WriteFile(late, fmt.Appendf(nil, `{"role":"user","content":"late newest","time":%d}
`, base.Add(2*time.Minute).UnixMilli()), 0o644); err != nil {
t.Fatal(err)
}
// Invert file mtimes: session activity should keep each session grouped
@@ -10792,8 +10784,8 @@ func TestScanPromptHistoryPaginatesCurrentSessionBeforeCrossSession(t *testing.T
other := filepath.Join(dir, "other.jsonl")
var lines []byte
for i := range 55 {
lines = append(lines, []byte(fmt.Sprintf(`{"role":"user","content":"current %d"}
`, i))...)
lines = append(lines, fmt.Appendf(nil, `{"role":"user","content":"current %d"}
`, i)...)
}
if err := os.WriteFile(current, lines, 0o644); err != nil {
t.Fatal(err)
@@ -10867,8 +10859,8 @@ func TestScanPromptHistoryFromDirReadsAllEntriesForInternalHelper(t *testing.T)
dir := t.TempDir()
var lines []byte
for i := range 250 {
lines = append(lines, []byte(fmt.Sprintf(`{"role":"user","content":"prompt %d"}
`, i))...)
lines = append(lines, fmt.Appendf(nil, `{"role":"user","content":"prompt %d"}
`, i)...)
}
if err := os.WriteFile(filepath.Join(dir, "many.jsonl"), lines, 0o644); err != nil {
t.Fatal(err)
+1 -1
View File
@@ -155,7 +155,7 @@ func TestBridgeTakeoverSwitchAnnouncesReleaseToOldTab(t *testing.T) {
t.Fatalf("switch to B: %v", err)
}
seen := map[string]bool{}
for i := 0; i < 2; i++ {
for range 2 {
select {
case got := <-env.announced:
seen[got[0]] = true
+1 -1
View File
@@ -693,7 +693,7 @@ func postFeishuInstallFormResult(base string, body map[string]string) (map[strin
for k, v := range body {
reqBody.Set(k, v)
}
req, err := http.NewRequestWithContext(ctx, "POST", strings.TrimRight(base, "/")+"/oauth/v1/app/registration", strings.NewReader(reqBody.Encode()))
req, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(base, "/")+"/oauth/v1/app/registration", strings.NewReader(reqBody.Encode()))
if err != nil {
return nil, 0, err
}
+2 -2
View File
@@ -114,12 +114,12 @@ func assertRequiredJSONSlicesNonNil(t *testing.T, path string, value reflect.Val
if value.Kind() == reflect.Slice && value.IsNil() {
t.Fatalf("%s is a nil slice; JSON contract requires []", path)
}
for i := 0; i < value.Len(); i++ {
for i := range value.Len() {
assertRequiredJSONSlicesNonNil(t, path, value.Index(i))
}
case reflect.Struct:
typ := value.Type()
for i := 0; i < value.NumField(); i++ {
for i := range value.NumField() {
fieldType := typ.Field(i)
if fieldType.PkgPath != "" {
continue
+1 -1
View File
@@ -230,7 +230,7 @@ func TestCheckpointsForTabLimitsCumulativeFilePreview(t *testing.T) {
}
content := "old"
files := make([]checkpoint.FileSnap, 0, checkpointFilePreviewLimit+5)
for i := 0; i < checkpointFilePreviewLimit+5; i++ {
for i := range checkpointFilePreviewLimit + 5 {
files = append(files, checkpoint.FileSnap{Path: "file-" + strconv.Itoa(1000+i) + ".txt", Content: &content})
}
now := time.Now()
+1 -1
View File
@@ -534,7 +534,7 @@ func cleanupOwnedWindowsUpdateDirectory(path string, owner os.FileInfo) error {
if path == "" || owner == nil || !owner.IsDir() {
return fmt.Errorf("Windows update cleanup identity is incomplete")
}
for attempt := 0; attempt < 16; attempt++ {
for attempt := range 16 {
cleanup := fmt.Sprintf("%s.reasonix-cleanup-%d-%d", path, time.Now().UTC().UnixNano(), attempt)
from, err := windows.UTF16PtrFromString(path)
if err != nil {
+1 -1
View File
@@ -155,7 +155,7 @@ func baseCrashReport(kind string) crashReport {
}
func topFrameFromStack(stack string) string {
for _, line := range strings.Split(stack, "\n") {
for line := range strings.SplitSeq(stack, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
+3 -5
View File
@@ -107,11 +107,9 @@ func TestWritePendingReportQueueIsBoundedUnderConcurrentWriters(t *testing.T) {
var done sync.WaitGroup
var successes atomic.Int32
for i := 0; i < writers; i++ {
for range writers {
ready.Add(1)
done.Add(1)
go func() {
defer done.Done()
done.Go(func() {
report := baseCrashReport("performance")
report.Source = "native.watchdog"
report.Label = "mac.main_thread.hang"
@@ -121,7 +119,7 @@ func TestWritePendingReportQueueIsBoundedUnderConcurrentWriters(t *testing.T) {
if writePendingReport(report, false) {
successes.Add(1)
}
}()
})
}
ready.Wait()
close(start)
+2 -3
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log/slog"
"maps"
"strings"
"sync"
"time"
@@ -163,9 +164,7 @@ func (a *App) deferredRebuildTick(markIdle bool) bool {
return true
}
pending := make(map[string]string, len(d.pending))
for id, setting := range d.pending {
pending[id] = setting
}
maps.Copy(pending, d.pending)
d.mu.Unlock()
for tabID, setting := range pending {
+3 -3
View File
@@ -28,7 +28,7 @@ func collectDeviceInfo() deviceInfo {
}
func parseCPUModel(cpuinfo string) string {
for _, line := range strings.Split(cpuinfo, "\n") {
for line := range strings.SplitSeq(cpuinfo, "\n") {
if name, ok := strings.CutPrefix(line, "model name"); ok {
if _, v, ok := strings.Cut(name, ":"); ok {
return strings.TrimSpace(v)
@@ -39,7 +39,7 @@ func parseCPUModel(cpuinfo string) string {
}
func parseMemTotalBytes(meminfo string) uint64 {
for _, line := range strings.Split(meminfo, "\n") {
for line := range strings.SplitSeq(meminfo, "\n") {
if rest, ok := strings.CutPrefix(line, "MemTotal:"); ok {
kb, err := strconv.ParseUint(strings.TrimSuffix(strings.TrimSpace(rest), " kB"), 10, 64)
if err != nil {
@@ -52,7 +52,7 @@ func parseMemTotalBytes(meminfo string) uint64 {
}
func parseOSReleasePrettyName(osRelease string) string {
for _, line := range strings.Split(osRelease, "\n") {
for line := range strings.SplitSeq(osRelease, "\n") {
if v, ok := strings.CutPrefix(line, "PRETTY_NAME="); ok {
return strings.Trim(strings.TrimSpace(v), `"`)
}
-2
View File
@@ -81,7 +81,6 @@ func TestExportErrorsDoNotExposeSelectedDirectory(t *testing.T) {
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
err := test.run(test.path)
if err == nil {
@@ -290,7 +289,6 @@ func TestConcurrentMultipartExportsHaveSingleCompleteWinner(t *testing.T) {
var ready sync.WaitGroup
ready.Add(len(batches))
for _, batch := range batches {
batch := batch
go func() {
ready.Done()
<-start
+1 -1
View File
@@ -48,7 +48,7 @@ func darwinInstalledApplicationIndex() map[string]string {
defer cancel()
out, err := exec.CommandContext(ctx, "/usr/bin/mdfind", "kMDItemContentType == 'com.apple.application-bundle'").Output()
if err == nil {
for _, line := range strings.Split(string(out), "\n") {
for line := range strings.SplitSeq(string(out), "\n") {
add(line)
}
}
+2 -8
View File
@@ -24,10 +24,7 @@ var (
)
func recordMainThreadHeartbeat(t time.Time) {
elapsed := t.Sub(mainThreadClockBase)
if elapsed < 0 {
elapsed = 0
}
elapsed := max(t.Sub(mainThreadClockBase), 0)
mainThreadLastHeartbeatElapsed.Store(int64(elapsed))
mainThreadLastHeartbeatWall.Store(t.UnixNano())
}
@@ -38,10 +35,7 @@ func mainThreadHeartbeatAge(now time.Time) (time.Duration, time.Time, bool) {
if lastWall <= 0 {
return 0, time.Time{}, false
}
age := now.Sub(mainThreadClockBase) - lastElapsed
if age < 0 {
age = 0
}
age := max(now.Sub(mainThreadClockBase)-lastElapsed, 0)
return age, time.Unix(0, lastWall), true
}
+1 -4
View File
@@ -113,10 +113,7 @@ func windowClassName(hwnd uintptr) string {
}
func windowMessageLoopResponsive(hwnd uintptr, timeout time.Duration) bool {
timeoutMS := timeout.Milliseconds()
if timeoutMS < 250 {
timeoutMS = 250
}
timeoutMS := max(timeout.Milliseconds(), 250)
var result uintptr
ok, _, _ := sendMessageTimeoutProc.Call(
hwnd,
+1 -1
View File
@@ -12,7 +12,7 @@ func TestCurrentProcessTopLevelWindowReusesEnumCallback(t *testing.T) {
// Go's Windows runtime currently supports a finite callback table. The old
// implementation allocated one callback per poll and exhausted that table
// during a normal long-running session.
for i := 0; i < 2100; i++ {
for range 2100 {
_ = currentProcessTopLevelWindow()
}
}
+6 -6
View File
@@ -319,7 +319,7 @@ func (e *HeartbeatEngine) executeTask(t HeartbeatTask) HeartbeatTask {
// Wait for the tab's controller to be built (it's started
// asynchronously in a goroutine by openTopicTab).
var ctrl heartbeatRuntimeStatus
for i := 0; i < 40; i++ {
for range 40 {
if candidate := e.app.ctrlByTabID(tabMeta.ID); candidate != nil {
ctrl = candidate
break
@@ -597,11 +597,11 @@ type heartbeatSchedule struct {
}
func parseHeartbeatSchedule(interval string) (heartbeatSchedule, bool) {
idx := strings.Index(interval, "|")
if idx < 0 {
_, after, ok0 := strings.Cut(interval, "|")
if !ok0 {
return heartbeatSchedule{}, false
}
raw := strings.TrimSpace(interval[idx+1:])
raw := strings.TrimSpace(after)
if raw == "" {
return heartbeatSchedule{}, false
}
@@ -625,7 +625,7 @@ func parseHeartbeatSchedule(interval string) (heartbeatSchedule, bool) {
case "daily":
return s, true
case "weekly", "biweekly":
for _, part := range strings.Split(rule, ",") {
for part := range strings.SplitSeq(rule, ",") {
if wd, ok := parseHeartbeatWeekday(part); ok {
s.days = append(s.days, wd)
}
@@ -681,7 +681,7 @@ func previousHeartbeatScheduleAt(t HeartbeatTask, now time.Time) (time.Time, boo
func previousHeartbeatWeeklyAt(s heartbeatSchedule, now time.Time, windowDays int, anchor time.Time) (time.Time, bool) {
var best time.Time
for offset := 0; offset < windowDays; offset++ {
for offset := range windowDays {
day := now.AddDate(0, 0, -offset)
for _, wd := range s.days {
if day.Weekday() != wd {
+2 -2
View File
@@ -155,7 +155,7 @@ func TestHeartbeatExecuteTaskPersistsFreshConversationTopicID(t *testing.T) {
app := NewApp()
app.ctx = context.Background()
app.readyHook = func() {}
app.runtimeEvents.emit = func(context.Context, string, ...interface{}) {}
app.runtimeEvents.emit = func(context.Context, string, ...any) {}
engine := &HeartbeatEngine{
app: app,
pendingTopics: map[string]heartbeatPendingTopic{},
@@ -236,7 +236,7 @@ func TestHeartbeatExecuteTaskSkipsPendingPrompt(t *testing.T) {
app := NewApp()
app.ctx = context.Background()
app.readyHook = func() {}
app.runtimeEvents.emit = func(context.Context, string, ...interface{}) {}
app.runtimeEvents.emit = func(context.Context, string, ...any) {}
engine := &HeartbeatEngine{
app: app,
pendingTopics: map[string]heartbeatPendingTopic{},
+3 -3
View File
@@ -23,7 +23,7 @@ func syntheticHistoryMessages(turns, outputSize int, toolName string, failed boo
if failed {
output = "error: " + output
}
for i := 0; i < turns; i++ {
for i := range turns {
callID := fmt.Sprintf("call_%d", i)
msgs = append(msgs,
provider.Message{Role: provider.RoleUser, Content: fmt.Sprintf("prompt %d", i)},
@@ -66,7 +66,7 @@ func BenchmarkHistoryMessagesSynthetic(b *testing.B) {
b.ReportAllocs()
b.SetBytes(inputBytes)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
got := historyMessages(msgs, func(content string) string { return content })
if len(got) == 0 {
b.Fatal("empty history")
@@ -84,7 +84,7 @@ func BenchmarkHistoryMessagesMarshalSynthetic(b *testing.B) {
b.ReportAllocs()
b.SetBytes(inputBytes)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
got := historyMessages(msgs, func(content string) string { return content })
encoded, err := json.Marshal(got)
if err != nil {
+2 -6
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
fileencoding "reasonix/internal/fileutil/encoding"
@@ -127,12 +128,7 @@ func hookEventNames() []string {
}
func validHookEvent(event hook.Event) bool {
for _, e := range hook.Events {
if event == e {
return true
}
}
return false
return slices.Contains(hook.Events, event)
}
func hookConfigView(event hook.Event, cfg hook.HookConfig) HookConfigView {
+1 -1
View File
@@ -64,7 +64,7 @@ func TestMain(m *testing.M) {
// contexts tests use, killing the process from any emitting code path.
// Tests that assert on runtime events install their own capture through
// the per-instance runtimeEvents.emit hook, which takes precedence.
runtimeEventsEmitFallback = func(context.Context, string, ...interface{}) {}
runtimeEventsEmitFallback = func(context.Context, string, ...any) {}
code := m.Run()
os.RemoveAll(dir)
os.Exit(code)
+4 -5
View File
@@ -9,6 +9,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@@ -102,10 +103,8 @@ func countBucket(n int) string {
func knownBucket(value string, allowed ...string) string {
value = strings.ToLower(strings.TrimSpace(value))
for _, ok := range allowed {
if value == ok {
return value
}
if slices.Contains(allowed, value) {
return value
}
return "other"
}
@@ -433,7 +432,7 @@ func (m *metricsAggregator) observeRecoveryMetrics(stats recovery.Metrics) {
return
}
add := func(signal string, n int64) {
for i := int64(0); i < n; i++ {
for range n {
m.inc(signal, "total")
}
}
+8 -8
View File
@@ -36,14 +36,14 @@ func TestSessionLeaseHelpersConcurrentAccess(t *testing.T) {
wg.Add(3)
go func() {
defer wg.Done()
for i := 0; i < iterations; i++ {
for range iterations {
_ = tabA.ensureSessionLease(path)
_ = tabA.sessionLeaseRuntimeKey()
}
}()
go func() {
defer wg.Done()
for i := 0; i < iterations; i++ {
for range iterations {
// The applyRuntimeTab transfer shape: move A's lease to B and back.
tabB.adoptSessionLease(tabA.takeSessionLease())
tabA.adoptSessionLease(tabB.takeSessionLease())
@@ -51,7 +51,7 @@ func TestSessionLeaseHelpersConcurrentAccess(t *testing.T) {
}()
go func() {
defer wg.Done()
for i := 0; i < iterations; i++ {
for range iterations {
tabA.releaseSessionLease()
}
}()
@@ -142,13 +142,13 @@ func TestTabEventSinkEmitConcurrentRebind(t *testing.T) {
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < iterations; i++ {
for range iterations {
sink.Emit(event.Event{Kind: event.Notice, Text: "hammer"})
}
}()
go func() {
defer wg.Done()
for i := 0; i < iterations; i++ {
for i := range iterations {
sink.setBinding(fmt.Sprintf("tab-%d", i%2), nil)
sink.clearContext()
}
@@ -170,7 +170,7 @@ func TestModeRebuildAndABANavigationFenceLeaseFailureAndOldEpochEvent(t *testing
continueCommit := make(chan struct{})
oldEvent := make(chan wireEventTab, 1)
var capturedOldEvent wireEventTab
oldSink.runtimeEvents.emit = func(_ context.Context, name string, payload ...interface{}) {
oldSink.runtimeEvents.emit = func(_ context.Context, name string, payload ...any) {
if name != eventChannel || len(payload) != 1 {
return
}
@@ -573,7 +573,7 @@ func TestMetaForTabConcurrentWithBuildSwap(t *testing.T) {
done := make(chan struct{})
go func() {
defer close(done)
for i := 0; i < iterations; i++ {
for i := range iterations {
app.mu.Lock()
tab.Ready = !tab.Ready
tab.Label = fmt.Sprintf("model-%d", i)
@@ -584,7 +584,7 @@ func TestMetaForTabConcurrentWithBuildSwap(t *testing.T) {
app.mu.Unlock()
}
}()
for i := 0; i < iterations; i++ {
for range iterations {
meta := app.MetaForTab("tab")
if meta.EventChannel == "" {
t.Fatal("MetaForTab returned zero meta for a live tab")
+1 -3
View File
@@ -1,7 +1,6 @@
package main
import (
"context"
"os"
"path/filepath"
"testing"
@@ -106,8 +105,7 @@ func TestRecoveryGCFirstSweepWaitsForTabRestore(t *testing.T) {
}
_, branchPath := forkCoveredRecoveryBranch(t, dir, "startup")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
app := &App{
ctx: ctx,
tabs: map[string]*WorkspaceTab{},
+3 -8
View File
@@ -5,6 +5,7 @@ import (
"io"
"os"
"path/filepath"
"slices"
"sync"
"testing"
"time"
@@ -77,7 +78,7 @@ func TestReloadRuntimeSwapsAndClosesOldAfterSwap(t *testing.T) {
app.readyHook = func() {}
var fenceMu sync.Mutex
var fenceNames []string
app.runtimeEvents.emit = func(_ context.Context, name string, _ ...interface{}) {
app.runtimeEvents.emit = func(_ context.Context, name string, _ ...any) {
fenceMu.Lock()
fenceNames = append(fenceNames, name)
fenceMu.Unlock()
@@ -131,13 +132,7 @@ func TestReloadRuntimeSwapsAndClosesOldAfterSwap(t *testing.T) {
deadline := time.Now().Add(2 * time.Second)
for {
fenceMu.Lock()
found := false
for _, name := range fenceNames {
if name == "runtime:rebuilt" {
found = true
break
}
}
found := slices.Contains(fenceNames, "runtime:rebuilt")
fenceMu.Unlock()
if found {
break
+1 -1
View File
@@ -165,7 +165,7 @@ func TestConfirmRemoteSecretDelegatesWithoutPersisting(t *testing.T) {
func TestRemoteStatusBridgesToAsyncEmitter(t *testing.T) {
a := &App{ctx: context.Background()}
events := make(chan runtimeEventEnvelope, 4)
a.runtimeEvents.emit = func(ctx context.Context, name string, payload ...interface{}) {
a.runtimeEvents.emit = func(ctx context.Context, name string, payload ...any) {
events <- runtimeEventEnvelope{ctx: ctx, name: name, payload: payload}
}
a.onStatus(RemoteConnectionStatusView{HostID: "box", State: "connected"})
+1 -1
View File
@@ -194,7 +194,7 @@ func canonicalRemoteSSHHost(host string) (string, error) {
if trimmed == "" || len(trimmed) > 253 {
return "", errors.New("remote SSH host is invalid")
}
for _, label := range strings.Split(trimmed, ".") {
for label := range strings.SplitSeq(trimmed, ".") {
if !remoteDNSLabelPattern.MatchString(label) {
return "", errors.New("remote SSH host is invalid")
}
+3 -3
View File
@@ -100,10 +100,10 @@ func TestRemoteHostStoreLoadsV1AsConfigAndWritesV2OnMutation(t *testing.T) {
if err != nil {
t.Fatal(err)
}
raw := []byte(fmt.Sprintf(
raw := fmt.Appendf(nil,
`{"version":1,"hosts":[{"id":%q,"alias":%q,"label":%q,"sshConfigPath":%q,"clientInstanceId":%q,"resumeLeaseId":"lease_legacy","layoutRef":"layout_legacy"}]}`,
entry.ID, entry.Alias, entry.Label, configPath, entry.ClientInstanceID,
))
)
if err := os.WriteFile(path, raw, 0o600); err != nil {
t.Fatal(err)
}
@@ -213,7 +213,7 @@ func TestRemoteHostStoreConcurrentInstancesDoNotLoseUpdates(t *testing.T) {
const count = 48
var wg sync.WaitGroup
errorsCh := make(chan error, count)
for i := 0; i < count; i++ {
for i := range count {
wg.Add(1)
go func(i int) {
defer wg.Done()
+2 -4
View File
@@ -373,15 +373,13 @@ func TestHostKeyPromptsAreSerializedForGlobalDialog(t *testing.T) {
prompts = append(prompts, pendingPrompt{hostID: hostID, prompt: mgr.hostKeyPrompt(hostID, mh)})
}
for _, pending := range prompts {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
_, _ = pending.prompt(ctx, remote.HostKeyQuestion{
Address: pending.hostID + ":22",
KeyType: "ssh-ed25519",
Fingerprint: pending.hostID,
})
}()
})
}
first := <-sink.statuses
+2 -2
View File
@@ -574,8 +574,8 @@ func (a *App) domReadyRemoteWindow() {
func (a *App) secondInstanceRemoteWindow(data options.SecondInstanceData) {
ticket := ""
for _, arg := range data.Args {
if strings.HasPrefix(arg, remoteWindowTicketArgPrefix) {
ticket = strings.TrimPrefix(arg, remoteWindowTicketArgPrefix)
if after, ok := strings.CutPrefix(arg, remoteWindowTicketArgPrefix); ok {
ticket = after
break
}
}
+1 -2
View File
@@ -289,8 +289,7 @@ func TestRemoteWindowOwnerWaitDetectsParentExit(t *testing.T) {
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
exited := make(chan bool, 1)
go func() { exited <- waitForRemoteWindowOwnerExit(ctx, cmd.Process.Pid) }()
if err := cmd.Process.Kill(); err != nil {
+2 -2
View File
@@ -59,14 +59,14 @@ func TestRuntimeRebuildsEmitRuntimeRebuiltForTab(t *testing.T) {
// The App-level queue must stay silent: ordering against the tab's agent
// events only holds when the notice rides the tab sink's own queue, so a
// notice showing up here means the routing regressed to the fallback.
app.runtimeEvents.emit = func(_ context.Context, name string, _ ...interface{}) {
app.runtimeEvents.emit = func(_ context.Context, name string, _ ...any) {
if name == "runtime:rebuilt" {
mu.Lock()
rebuilt = append(rebuilt, "VIA-APP-QUEUE")
mu.Unlock()
}
}
sinkEmit := func(_ context.Context, name string, payload ...interface{}) {
sinkEmit := func(_ context.Context, name string, payload ...any) {
if name != "runtime:rebuilt" {
return
}
+8 -13
View File
@@ -172,7 +172,7 @@ func TestParallelDesktopTabsPersistCompleteTranscriptsAcrossReload(t *testing.T)
tabOrder := make([]string, 0, tabCount)
controllers := make([]*control.Controller, 0, tabCount)
for tabIndex := 0; tabIndex < tabCount; tabIndex++ {
for tabIndex := range tabCount {
id := fmt.Sprintf("parallel-%02d", tabIndex)
path := filepath.Join(dir, id+".jsonl")
prov := &capturingProvider{}
@@ -207,25 +207,20 @@ func TestParallelDesktopTabsPersistCompleteTranscriptsAcrossReload(t *testing.T)
errs := make(chan error, tabCount+1)
var wg sync.WaitGroup
for tabIndex, ctrl := range controllers {
tabIndex, ctrl := tabIndex, ctrl
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
<-start
for turn := 0; turn < turnsPerTab; turn++ {
for turn := range turnsPerTab {
input := fmt.Sprintf("tab-%02d-turn-%02d", tabIndex, turn)
if err := ctrl.RunTurn(context.Background(), input); err != nil {
errs <- fmt.Errorf("%s: %w", input, err)
return
}
}
}()
})
}
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
<-start
for round := 0; round < 3; round++ {
for range 3 {
for _, id := range tabOrder {
if err := app.SetActiveTab(id); err != nil {
errs <- fmt.Errorf("switch to %s: %w", id, err)
@@ -233,7 +228,7 @@ func TestParallelDesktopTabsPersistCompleteTranscriptsAcrossReload(t *testing.T)
}
}
}
}()
})
close(start)
wg.Wait()
@@ -269,7 +264,7 @@ func TestParallelDesktopTabsPersistCompleteTranscriptsAcrossReload(t *testing.T)
if msgs[0].Role != provider.RoleSystem || msgs[0].Content != systemText {
t.Fatalf("%s system message = %+v", id, msgs[0])
}
for turn := 0; turn < turnsPerTab; turn++ {
for turn := range turnsPerTab {
user := msgs[1+turn*2]
assistant := msgs[2+turn*2]
wantUser := fmt.Sprintf("tab-%02d-turn-%02d", tabIndex, turn)
+1 -1
View File
@@ -17,7 +17,7 @@ import (
func saveSnapshotTurns(t *testing.T, path string, turns int) *agent.Session {
t.Helper()
s := agent.NewSession("sys")
for i := 0; i < turns; i++ {
for i := range turns {
s.Add(provider.Message{Role: provider.RoleUser, Content: "prompt " + string(rune('a'+i))})
s.Add(provider.Message{Role: provider.RoleAssistant, Content: "reply"})
if err := s.SaveSnapshot(path); err != nil {
+1 -1
View File
@@ -354,7 +354,7 @@ func reserveUniqueSessionTrashItemDir(dir, key string) (string, error) {
return "", err
}
stem := strings.TrimSuffix(key, ".jsonl")
for i := 0; i < 100; i++ {
for i := range 100 {
name := fmt.Sprintf("%s.jsonl-deleted-%d-%02d", stem, time.Now().UnixNano(), i)
itemDir := filepath.Join(root, name)
if err := os.Mkdir(itemDir, 0o755); err == nil {
+5 -5
View File
@@ -30,7 +30,7 @@ func occupyReadFileWithTimeoutSlots(t *testing.T) func() {
return
}
released = true
for i := 0; i < filled; i++ {
for range filled {
<-readFileWithTimeoutSlots
}
}
@@ -1208,7 +1208,7 @@ func TestRecordSessionPlannerDisplayConcurrentPreservesEverySession(t *testing.T
start := make(chan struct{})
errs := make(chan error, writers)
var wg sync.WaitGroup
for i := 0; i < writers; i++ {
for i := range writers {
wg.Add(1)
go func(i int) {
defer wg.Done()
@@ -1232,7 +1232,7 @@ func TestRecordSessionPlannerDisplayConcurrentPreservesEverySession(t *testing.T
if len(got) != writers {
t.Fatalf("planner display sessions = %d, want %d", len(got), writers)
}
for i := 0; i < writers; i++ {
for i := range writers {
key := fmt.Sprintf("session-%02d.jsonl", i)
if len(got[key]) != 1 || len(got[key][0].Messages) != 1 || got[key][0].Messages[0].Content != fmt.Sprintf("answer-%02d", i) {
t.Fatalf("planner display %s = %+v", key, got[key])
@@ -1504,7 +1504,7 @@ func TestRecordSessionDisplaySerializesConcurrentTabs(t *testing.T) {
const tabs = 32
errs := make(chan error, tabs)
var wg sync.WaitGroup
for i := 0; i < tabs; i++ {
for i := range tabs {
wg.Add(1)
go func(i int) {
defer wg.Done()
@@ -1523,7 +1523,7 @@ func TestRecordSessionDisplaySerializesConcurrentTabs(t *testing.T) {
}
got := loadSessionDisplays(dir)
for i := 0; i < tabs; i++ {
for i := range tabs {
key := fmt.Sprintf("tab-%02d.jsonl", i)
content := fmt.Sprintf("expanded-%02d", i)
if display := got[key][messageDisplayKey(content)]; display != fmt.Sprintf("display-%02d", i) {
+6 -7
View File
@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"runtime"
"slices"
"sort"
"strings"
"sync"
@@ -1687,13 +1688,13 @@ func configDeclaresProviderAccess(path string) bool {
if err != nil {
return false
}
for _, line := range strings.Split(string(body), "\n") {
for line := range strings.SplitSeq(string(body), "\n") {
if before, _, ok := strings.Cut(line, "#"); ok {
line = before
}
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "provider_access") {
rest := strings.TrimSpace(strings.TrimPrefix(line, "provider_access"))
if after, ok := strings.CutPrefix(line, "provider_access"); ok {
rest := strings.TrimSpace(after)
return strings.HasPrefix(rest, "=")
}
}
@@ -2321,10 +2322,8 @@ func providerVisionModels(models, visionModels []string) []string {
func providerDefaultForModels(currentDefault string, models []string) string {
currentDefault = strings.TrimSpace(currentDefault)
if currentDefault != "" {
for _, model := range models {
if model == currentDefault {
return currentDefault
}
if slices.Contains(models, currentDefault) {
return currentDefault
}
}
if len(models) > 0 {
+2 -2
View File
@@ -73,7 +73,7 @@ func (a *App) reapOrphanCodeGraph() {
ours := map[int]bool{}
out, err := exec.Command("pgrep", "-P", strconv.Itoa(myPID)).Output()
if err == nil {
for _, f := range strings.Fields(string(out)) {
for f := range strings.FieldsSeq(string(out)) {
if pid, err := strconv.Atoi(f); err == nil {
ours[pid] = true
}
@@ -85,7 +85,7 @@ func (a *App) reapOrphanCodeGraph() {
if err != nil {
return
}
for _, f := range strings.Fields(string(out)) {
for f := range strings.FieldsSeq(string(out)) {
pid, err := strconv.Atoi(f)
if err != nil || pid == myPID || ours[pid] {
continue
+5 -5
View File
@@ -110,7 +110,7 @@ func TestTabEventSinkDoesNotBlockOnRuntimeEventsEmit(t *testing.T) {
var calls atomic.Int32
sink := &tabEventSink{tabID: "tab", ctx: context.Background()}
sink.runtimeEvents.emit = func(_ context.Context, name string, payload ...interface{}) {
sink.runtimeEvents.emit = func(_ context.Context, name string, payload ...any) {
if name != eventChannel {
t.Errorf("event name = %q, want %q", name, eventChannel)
}
@@ -170,7 +170,7 @@ func TestEmitProjectTreeChangedDoesNotBlockOnRuntimeEventsEmit(t *testing.T) {
var calls atomic.Int32
app := &App{ctx: context.Background()}
app.runtimeEvents.emit = func(_ context.Context, name string, payload ...interface{}) {
app.runtimeEvents.emit = func(_ context.Context, name string, payload ...any) {
if name != "project-tree:changed" {
t.Errorf("event name = %q, want project-tree:changed", name)
}
@@ -221,7 +221,7 @@ func TestAsyncRuntimeEmitterDrainsBacklogInOrder(t *testing.T) {
var calls atomic.Int32
emitter := &asyncRuntimeEmitter{}
emitter.emit = func(_ context.Context, _ string, payload ...interface{}) {
emitter.emit = func(_ context.Context, _ string, payload ...any) {
if len(payload) != 1 {
t.Errorf("payload count = %d, want 1", len(payload))
return
@@ -239,7 +239,7 @@ func TestAsyncRuntimeEmitterDrainsBacklogInOrder(t *testing.T) {
}
ctx := context.Background()
for i := 0; i < backlog; i++ {
for i := range backlog {
emitter.Emit(ctx, "agent:event", strconv.Itoa(i))
}
@@ -250,7 +250,7 @@ func TestAsyncRuntimeEmitterDrainsBacklogInOrder(t *testing.T) {
}
close(release)
for i := 0; i < backlog; i++ {
for i := range backlog {
select {
case got := <-delivered:
if want := strconv.Itoa(i); got != want {
+27 -45
View File
@@ -9,8 +9,10 @@ import (
"errors"
"fmt"
"log/slog"
"maps"
"os"
"path/filepath"
"slices"
"sort"
"strings"
"sync"
@@ -308,15 +310,11 @@ func cloneSessionUsageStats(in sessionUsageStats) sessionUsageStats {
out := in
if len(in.Sources) > 0 {
out.Sources = make(map[string]usageSourceStats, len(in.Sources))
for source, stats := range in.Sources {
out.Sources[source] = stats
}
maps.Copy(out.Sources, in.Sources)
}
if len(in.sourceSessionCache) > 0 {
out.sourceSessionCache = make(map[string]sourceSessionCacheCounters, len(in.sourceSessionCache))
for source, counters := range in.sourceSessionCache {
out.sourceSessionCache[source] = counters
}
maps.Copy(out.sourceSessionCache, in.sourceSessionCache)
}
return out
}
@@ -1197,9 +1195,7 @@ func (t *WorkspaceTab) telemetrySnapshot() tabTelemetrySnapshot {
}
if len(t.usageTelemetry.Sources) > 0 {
usage.Sources = make(map[string]usageSourceStats, len(t.usageTelemetry.Sources))
for source, stats := range t.usageTelemetry.Sources {
usage.Sources[source] = stats
}
maps.Copy(usage.Sources, t.usageTelemetry.Sources)
}
usage.activeTurnStartedAt = 0
usage.sourceSessionCache = nil
@@ -1408,9 +1404,9 @@ func updateBufferedHistoryToolCallSummary(messages []*bufferedHistoryMessage, ca
if callID == "" {
return
}
for i := len(messages) - 1; i >= 0; i-- {
for j := range messages[i].message.ToolCalls {
call := &messages[i].message.ToolCalls[j]
for _, v := range slices.Backward(messages) {
for j := range v.message.ToolCalls {
call := &v.message.ToolCalls[j]
if call.ID != callID {
continue
}
@@ -1737,7 +1733,7 @@ func (s *tabEventSink) context() context.Context {
return s.ctx
}
func (s *tabEventSink) emitRuntimeEvent(name string, payload ...interface{}) {
func (s *tabEventSink) emitRuntimeEvent(name string, payload ...any) {
if s == nil {
return
}
@@ -1748,12 +1744,12 @@ func (s *tabEventSink) emitRuntimeEvent(name string, payload ...interface{}) {
s.runtimeEvents.Emit(ctx, name, payload...)
}
type runtimeEventEmitFunc func(context.Context, string, ...interface{})
type runtimeEventEmitFunc func(context.Context, string, ...any)
type runtimeEventEnvelope struct {
ctx context.Context
name string
payload []interface{}
payload []any
}
// asyncRuntimeEmitter decouples Wails' runtime event bridge from agent
@@ -1775,14 +1771,14 @@ type asyncRuntimeEmitter struct {
running bool
}
func (e *asyncRuntimeEmitter) Emit(ctx context.Context, name string, payload ...interface{}) {
func (e *asyncRuntimeEmitter) Emit(ctx context.Context, name string, payload ...any) {
if ctx == nil {
return
}
item := runtimeEventEnvelope{
ctx: ctx,
name: name,
payload: append([]interface{}(nil), payload...),
payload: append([]any(nil), payload...),
}
e.mu.Lock()
e.queue = append(e.queue, item)
@@ -2077,8 +2073,8 @@ func lastHistoryMessageIsUser(history []provider.Message) bool {
}
func hasPendingInterruptedRecovery(history []provider.Message) bool {
for i := len(history) - 1; i >= 0; i-- {
m := history[i]
for _, v := range slices.Backward(history) {
m := v
if m.LocalOnly && m.InterruptedTurn != nil {
return m.InterruptedTurn.Pending
}
@@ -2104,9 +2100,9 @@ func (s *tabEventSink) eventTabAndController() (*WorkspaceTab, control.SessionAP
}
func lastUserMessageContent(msgs []provider.Message) string {
for i := len(msgs) - 1; i >= 0; i-- {
if msgs[i].Role == provider.RoleUser {
return agent.UserMessageText(msgs[i])
for _, v := range slices.Backward(msgs) {
if v.Role == provider.RoleUser {
return agent.UserMessageText(v)
}
}
return ""
@@ -2892,7 +2888,7 @@ func createEmptySessionFile(dir, model string) (string, error) {
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", err
}
for i := 0; i < 3; i++ {
for range 3 {
path := agent.NewSessionPath(dir, model)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o644)
if err == nil {
@@ -3232,10 +3228,7 @@ func (a *App) closeTab(tabID string, allowDetach bool) error {
if wasActive {
a.activeTabID = ""
if len(a.tabOrder) > 0 {
nextIndex := closedIndex
if nextIndex < 0 {
nextIndex = 0
}
nextIndex := max(closedIndex, 0)
if nextIndex >= len(a.tabOrder) {
nextIndex = len(a.tabOrder) - 1
}
@@ -4673,7 +4666,7 @@ func autoTopicTitleProposalFromSession(path string) autoTopicTitleProposal {
if title == "" {
return autoTopicTitleProposal{}
}
sum := sha256.Sum256([]byte(fmt.Sprintf("%d\x00%s", stage, strings.Join(basis, "\x00"))))
sum := sha256.Sum256(fmt.Appendf(nil, "%d\x00%s", stage, strings.Join(basis, "\x00")))
return autoTopicTitleProposal{
Title: title,
Stage: stage,
@@ -4799,10 +4792,7 @@ func topicTitleFromUserTurns(users []string) string {
continue
}
runes := len([]rune(title))
score := runes
if score > 24 {
score = 24
}
score := min(runes, 24)
if i == 0 {
score += 3
}
@@ -5443,12 +5433,7 @@ func containsDesktopString(values []string, value string) bool {
if value == "" {
return false
}
for _, item := range uniqueStrings(values) {
if item == value {
return true
}
}
return false
return slices.Contains(uniqueStrings(values), value)
}
func pinnedTopicIDs(topicIDs []string, pinned []string) []string {
@@ -7484,7 +7469,7 @@ func (a *App) emitProjectTreeChangedEvent() {
a.emitRuntimeEvent("project-tree:changed")
}
func (a *App) emitRuntimeEvent(name string, payload ...interface{}) {
func (a *App) emitRuntimeEvent(name string, payload ...any) {
if a == nil || a.ctx == nil {
return
}
@@ -7875,7 +7860,7 @@ func (a *App) ListProjectTree() []ProjectNode {
continue
}
pendingLoads++
dir := dir // capture
// capture
cacheToken := projectSessionCache.versionToken(dir)
go func() {
result := sessionDirLoadResult{dir: dir}
@@ -8078,17 +8063,14 @@ func (a *App) ListProjectTree() []ProjectNode {
projectTopicResults := make([]projectTopics, len(f.Projects))
var topicLoadWg sync.WaitGroup
for i, p := range f.Projects {
i, p := i, p
topicLoadWg.Add(1)
go func() {
defer topicLoadWg.Done()
topicLoadWg.Go(func() {
projectTopicResults[i] = projectTopics{
project: p,
titles: loadTopicTitles(p.Root),
sources: loadTopicTitleSources(p.Root),
createdAts: loadTopicCreatedAts(p.Root),
}
}()
})
}
topicLoadWg.Wait()
for _, loaded := range projectTopicResults {
+2 -2
View File
@@ -57,9 +57,9 @@ func TestDisplayTurnBufferStreamingAllocationsStayNearLinear(t *testing.T) {
chunk := strings.Repeat("x", chunkSize)
result := testing.Benchmark(func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for range b.N {
var buffer displayTurnBuffer
for part := 0; part < chunks; part++ {
for range chunks {
recordHistoryDisplayEvent(&buffer, event.Event{Kind: event.Text, Text: chunk})
}
messages := buffer.materialize()
+5 -7
View File
@@ -33,7 +33,7 @@ func testAppWithOrderedTabs(t *testing.T, active string, ids ...string) *App {
}
func installNoopRuntimeEvents(app *App, sinks ...*tabEventSink) {
emit := func(context.Context, string, ...interface{}) {}
emit := func(context.Context, string, ...any) {}
if app != nil {
app.runtimeEvents.emit = emit
}
@@ -664,18 +664,16 @@ func TestListTabsRepairsStaleOrderWithoutRacing(t *testing.T) {
if testing.Short() {
iterations = 5
}
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 8 {
wg.Go(func() {
<-start
for j := 0; j < iterations; j++ {
for range iterations {
if got := strings.Join(tabIDs(app.ListTabs()), ","); got != "a,b,c" {
errs <- got
return
}
}
}()
})
}
close(start)
wg.Wait()
+3 -3
View File
@@ -224,10 +224,10 @@ func TestProjectTreeShowsBackgroundJobStatus(t *testing.T) {
func TestBackgroundJobNoticeForcesProjectTreeRefresh(t *testing.T) {
isolateDesktopUserDirs(t)
var refreshes int32
var refreshes atomic.Int32
app := NewApp()
app.projectTreeChangedHook = func() {
atomic.AddInt32(&refreshes, 1)
refreshes.Add(1)
}
app.tabs["job"] = &WorkspaceTab{
ID: "job",
@@ -239,7 +239,7 @@ func TestBackgroundJobNoticeForcesProjectTreeRefresh(t *testing.T) {
sink := &tabEventSink{tabID: "job", app: app}
sink.Emit(event.Event{Kind: event.Notice, Level: event.LevelInfo, Text: "background bash finished: bash-1"})
if got := atomic.LoadInt32(&refreshes); got != 1 {
if got := refreshes.Load(); got != 1 {
t.Fatalf("project-tree refreshes = %d, want 1 for background job finish notice", got)
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ func TestTabEventSinkClearContextStopsEmission(t *testing.T) {
var mu sync.Mutex
var emitted int
s := &tabEventSink{tabID: "t"}
s.runtimeEvents.emit = func(context.Context, string, ...interface{}) {
s.runtimeEvents.emit = func(context.Context, string, ...any) {
mu.Lock()
emitted++
mu.Unlock()
+9 -13
View File
@@ -3971,18 +3971,16 @@ func TestLegacyMigrationConcurrentRunsHaveNoLostUpdates(t *testing.T) {
}
const n = 8
want := make(map[string]bool, n)
for i := 0; i < n; i++ {
for i := range n {
p := writeLegacySession(t, dir, fmt.Sprintf("legacy-%d.jsonl", i), "hi", time.Now())
want[legacySessionTopicID(p)] = true
}
var wg sync.WaitGroup
for i := 0; i < n; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range n {
wg.Go(func() {
migrateLegacySessionsIntoGlobalTopics(dir)
}()
})
}
wg.Wait()
@@ -4148,17 +4146,15 @@ func TestEnsureTopicIndexedConcurrentRunsHaveNoLostProjectUpdates(t *testing.T)
const n = 12
start := make(chan struct{})
var wg sync.WaitGroup
for i := 0; i < n; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
for i := range n {
wg.Go(func() {
<-start
topicID := fmt.Sprintf("topic_recovered_%02d", i)
if err := ensureTopicIndexed("project", projectRoot, topicID, fmt.Sprintf("Recovered %02d", i), topicTitleSourceManual); err != nil {
t.Errorf("ensure topic indexed: %v", err)
}
}()
})
}
close(start)
wg.Wait()
@@ -4171,7 +4167,7 @@ func TestEnsureTopicIndexedConcurrentRunsHaveNoLostProjectUpdates(t *testing.T)
for _, child := range nodes[0].Children {
got[child.TopicID] = true
}
for i := 0; i < n; i++ {
for i := range n {
topicID := fmt.Sprintf("topic_recovered_%02d", i)
if !got[topicID] {
t.Fatalf("concurrent topic index recovery lost %q; children=%#v", topicID, nodes[0].Children)
+3 -5
View File
@@ -43,12 +43,10 @@ func TestTaskControlConcurrentInitializationReturnsOneService(t *testing.T) {
const callers = 16
services := make(chan *taskmonitor.ControlService, callers)
var wg sync.WaitGroup
for i := 0; i < callers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range callers {
wg.Go(func() {
services <- app.taskControl()
}()
})
}
wg.Wait()
close(services)
+1 -1
View File
@@ -23,7 +23,7 @@ func robustTempDir(t *testing.T) string {
}
t.Cleanup(func() {
var rmErr error
for i := 0; i < 100; i++ {
for range 100 {
if rmErr = os.RemoveAll(dir); rmErr == nil {
return
}
+3 -3
View File
@@ -291,13 +291,13 @@ func TestTerminalManagerCountsConcurrentStartsTowardLimit(t *testing.T) {
type result struct{ err error }
results := make(chan result, maxTerminalsPerWorkspace+1)
for i := 0; i < maxTerminalsPerWorkspace+1; i++ {
for range maxTerminalsPerWorkspace + 1 {
go func() {
_, err := manager.create("tab", "workspace", ".", terminalCommand{path: "shell", label: "shell"})
results <- result{err: err}
}()
}
for i := 0; i < maxTerminalsPerWorkspace; i++ {
for range maxTerminalsPerWorkspace {
select {
case <-entered:
case <-time.After(time.Second):
@@ -307,7 +307,7 @@ func TestTerminalManagerCountsConcurrentStartsTowardLimit(t *testing.T) {
close(release)
succeeded, limited := 0, 0
for i := 0; i < maxTerminalsPerWorkspace+1; i++ {
for range maxTerminalsPerWorkspace + 1 {
result := <-results
switch {
case result.err == nil:
+4 -4
View File
@@ -131,12 +131,12 @@ func decodeDataURLImage(dataURL string) (filename string, data []byte, err error
}
// data:[<mediatype>][;base64],<data>
rest := dataURL[len(prefix):]
comma := strings.IndexByte(rest, ',')
if comma < 0 {
before, after, ok := strings.Cut(rest, ",")
if !ok {
return "", nil, fmt.Errorf("invalid data URL")
}
meta := rest[:comma]
payload := rest[comma+1:]
meta := before
payload := after
parts := strings.Split(meta, ";")
mime := strings.TrimSpace(parts[0])
base64Enc := false
+3 -4
View File
@@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"maps"
"path/filepath"
"regexp"
"strings"
@@ -84,7 +85,7 @@ type ThemePackManifest struct {
Recipes ThemePackRecipes `json:"recipes"`
Background *ThemePackBackground `json:"background,omitempty"`
TaskBackground *ThemePackSceneBackground `json:"taskBackground,omitempty"`
Extra map[string]interface{} `json:"-"` // rejected on parse when present as unknown top-level
Extra map[string]any `json:"-"` // rejected on parse when present as unknown top-level
}
// ThemePackTokens holds optional light/dark semantic color overrides.
@@ -603,9 +604,7 @@ func copyStringMap(in map[string]string) map[string]string {
return nil
}
out := make(map[string]string, len(in))
for k, v := range in {
out[k] = v
}
maps.Copy(out, in)
return out
}
+2 -2
View File
@@ -631,8 +631,8 @@ func TestManifestJSONRoundTrip(t *testing.T) {
func writeTestPNG(t *testing.T, path string, w, h int) string {
t.Helper()
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
for y := range h {
for x := range w {
img.Set(x, y, color.RGBA{R: uint8(x), G: uint8(y), B: 40, A: 255})
}
}
+2 -2
View File
@@ -45,8 +45,8 @@ func testPluginThemeManifest(id, name string) *ThemePackManifest {
func testPNGBytes(t *testing.T) []byte {
t.Helper()
img := image.NewRGBA(image.Rect(0, 0, 8, 8))
for y := 0; y < 8; y++ {
for x := 0; x < 8; x++ {
for y := range 8 {
for x := range 8 {
img.Set(x, y, color.RGBA{R: uint8(x * 30), G: uint8(y * 30), B: 40, A: 255})
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ func TestDesktopTrayLoopRunsOnLockedOSThread(t *testing.T) {
done := make(chan struct{})
runDesktopTrayLoop(func() {
first := windows.GetCurrentThreadId()
for i := 0; i < 100; i++ {
for range 100 {
runtime.Gosched()
}
if got := windows.GetCurrentThreadId(); got != first {
+1 -1
View File
@@ -145,7 +145,7 @@ func helperErrorMessage(stdout, stderr []byte) string {
}
// Strip protocol lines from stderr diagnostics.
var kept []string
for _, line := range strings.Split(string(stderr), "\n") {
for line := range strings.SplitSeq(string(stderr), "\n") {
if _, ok := parseHelperPhaseLine(line); ok {
continue
}
+1 -1
View File
@@ -112,7 +112,7 @@ func isDpkgOwnedReasonix(absPath string) bool {
return false
}
// dpkg-query may return multiple lines for diversions; require an exact package hit.
for _, raw := range strings.Split(line, "\n") {
for raw := range strings.SplitSeq(line, "\n") {
raw = strings.TrimSpace(raw)
pkg, path, ok := strings.Cut(raw, ":")
if !ok {
+2 -2
View File
@@ -611,7 +611,7 @@ func completeMacHandoffHandshake(cfg macUpdateHandoffConfig) error {
}
func retainMacHandoffNode(path, suffix string) (string, error) {
for attempt := 0; attempt < 16; attempt++ {
for attempt := range 16 {
retained := fmt.Sprintf(
"%s.%s-%d-%d",
path,
@@ -636,7 +636,7 @@ func cleanupOwnedMacUpdateDirectory(path string, owner os.FileInfo) error {
if strings.TrimSpace(path) == "" || owner == nil || !owner.IsDir() {
return fmt.Errorf("macOS update cleanup identity is incomplete")
}
for attempt := 0; attempt < 16; attempt++ {
for attempt := range 16 {
cleanup := fmt.Sprintf("%s.reasonix-cleanup-%d-%d", path, time.Now().UTC().UnixNano(), attempt)
err := unix.RenameatxNp(unix.AT_FDCWD, path, unix.AT_FDCWD, cleanup, unix.RENAME_EXCL)
if err != nil {
+18 -18
View File
@@ -93,7 +93,7 @@ func TestValidateAssetInstallLayout(t *testing.T) {
}
func TestUpdaterWailsMethodContracts(t *testing.T) {
appType := reflect.TypeOf((*App)(nil))
appType := reflect.TypeFor[*App]()
tests := []struct {
name string
numIn int
@@ -1256,9 +1256,9 @@ func fastRetry(t *testing.T) {
func TestDownloadRecoversFromMidStreamReset(t *testing.T) {
fastRetry(t)
const body = "complete-installer-bytes"
var calls int32
var calls atomic.Int32
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
if atomic.AddInt32(&calls, 1) < int32(downloadAttempts) {
if calls.Add(1) < int32(downloadAttempts) {
// Mid-stream reset: promise 100 bytes, send a few, drop the socket —
// the client's body read fails with unexpected EOF, exactly the CN-IPv6
// "forcibly closed" case the retry exists for.
@@ -1283,16 +1283,16 @@ func TestDownloadRecoversFromMidStreamReset(t *testing.T) {
if string(data) != body {
t.Fatalf("got %q, want %q", data, body)
}
if n := atomic.LoadInt32(&calls); n != int32(downloadAttempts) {
if n := calls.Load(); n != int32(downloadAttempts) {
t.Fatalf("made %d attempts, want %d", n, downloadAttempts)
}
}
func TestDownloadGivesUpAfterCap(t *testing.T) {
fastRetry(t)
var calls int32
var calls atomic.Int32
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
atomic.AddInt32(&calls, 1)
calls.Add(1)
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
t.Errorf("hijack: %v", err)
@@ -1305,7 +1305,7 @@ func TestDownloadGivesUpAfterCap(t *testing.T) {
if _, err := download(context.Background(), srv.Client(), nil, srv.URL, 0, nil); err == nil {
t.Fatal("download should fail after exhausting retries")
}
if n := atomic.LoadInt32(&calls); n != int32(downloadAttempts) {
if n := calls.Load(); n != int32(downloadAttempts) {
t.Fatalf("made %d attempts, want %d", n, downloadAttempts)
}
}
@@ -1329,10 +1329,10 @@ func TestDownloadResumesWithRange(t *testing.T) {
fastRetry(t)
full := bytes.Repeat([]byte("0123456789"), 50) // 500 bytes
const cut = 200
var calls int32
var calls atomic.Int32
rangeCh := make(chan string, 4)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if atomic.AddInt32(&calls, 1) == 1 {
if calls.Add(1) == 1 {
// First attempt: promise the whole file, send a prefix, drop the socket.
conn, bw, err := w.(http.Hijacker).Hijack()
if err != nil {
@@ -1379,9 +1379,9 @@ func TestDownloadFallsBackToSecondClient(t *testing.T) {
primary := &http.Client{Transport: rtFunc(func(*http.Request) (*http.Response, error) {
return nil, errors.New("connection reset (ipv6)")
})}
var fbCalls int32
var fbCalls atomic.Int32
fallback := &http.Client{Transport: rtFunc(func(*http.Request) (*http.Response, error) {
atomic.AddInt32(&fbCalls, 1)
fbCalls.Add(1)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader(body)),
@@ -1397,7 +1397,7 @@ func TestDownloadFallsBackToSecondClient(t *testing.T) {
if string(data) != body {
t.Fatalf("got %q, want %q", data, body)
}
if atomic.LoadInt32(&fbCalls) == 0 {
if fbCalls.Load() == 0 {
t.Fatal("fallback client was never used after the primary failed")
}
}
@@ -1480,9 +1480,9 @@ func TestFetchBytesFallbackEscapesStalledPrimary(t *testing.T) {
func TestFetchBytesDoesNotRetryPermanentHTTPStatus(t *testing.T) {
fastRetry(t)
var calls int32
var calls atomic.Int32
client := &http.Client{Transport: rtFunc(func(*http.Request) (*http.Response, error) {
atomic.AddInt32(&calls, 1)
calls.Add(1)
return &http.Response{
StatusCode: http.StatusForbidden,
Status: "403 Forbidden",
@@ -1494,7 +1494,7 @@ func TestFetchBytesDoesNotRetryPermanentHTTPStatus(t *testing.T) {
if _, err := fetchBytes(context.Background(), client, "https://example.invalid/latest.json"); err == nil {
t.Fatal("fetchBytes should return a permanent HTTP error")
}
if got := atomic.LoadInt32(&calls); got != 1 {
if got := calls.Load(); got != 1 {
t.Fatalf("permanent HTTP error made %d requests, want 1", got)
}
}
@@ -1502,9 +1502,9 @@ func TestFetchBytesDoesNotRetryPermanentHTTPStatus(t *testing.T) {
func TestFetchBytesRejectsOversizeResponsesWithoutRetry(t *testing.T) {
fastRetry(t)
t.Run("declared content length", func(t *testing.T) {
var calls int32
var calls atomic.Int32
client := &http.Client{Transport: rtFunc(func(*http.Request) (*http.Response, error) {
atomic.AddInt32(&calls, 1)
calls.Add(1)
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
@@ -1523,7 +1523,7 @@ func TestFetchBytesRejectsOversizeResponsesWithoutRetry(t *testing.T) {
); !errors.Is(err, errUpdateResponseTooLarge) {
t.Fatalf("declared oversize error = %v, want errUpdateResponseTooLarge", err)
}
if got := atomic.LoadInt32(&calls); got != 1 {
if got := calls.Load(); got != 1 {
t.Fatalf("declared oversize response made %d requests, want 1", got)
}
})
+1 -1
View File
@@ -187,7 +187,7 @@ func TestSaveWindowStateConcurrentReports(t *testing.T) {
app := NewApp()
var wg sync.WaitGroup
for i := 0; i < 32; i++ {
for i := range 32 {
wg.Add(1)
go func(i int) {
defer wg.Done()
+1 -1
View File
@@ -351,7 +351,7 @@ func workspaceCurrentText(path string) (string, bool, bool, error) {
func tallyUnifiedPatch(patch string) (added, removed int) {
inHunk := false
for _, line := range strings.Split(patch, "\n") {
for line := range strings.SplitSeq(patch, "\n") {
switch {
case strings.HasPrefix(line, "@@"):
inHunk = true
+2 -2
View File
@@ -568,7 +568,7 @@ func BenchmarkDesktopSessionDir(b *testing.B) {
b.Fatal(err)
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for range b.N {
if desktopSessionDir(root) == "" {
b.Fatal("empty session dir")
}
@@ -950,7 +950,7 @@ func TestMediaTokenMaxEviction(t *testing.T) {
// Fill beyond max to trigger eviction of oldest.
var oldestToken string
for i := 0; i < mediaTokenMax+1; i++ {
for i := range mediaTokenMax + 1 {
tok := store.create(dir+"/test.png", "test.png", "image/png", "image", 4, time.Time{})
if i == 0 {
oldestToken = tok
+1 -1
View File
@@ -116,7 +116,7 @@ func TestNewSessionID(t *testing.T) {
func TestNewSessionIDUnique(t *testing.T) {
seen := map[string]bool{}
for i := 0; i < 100; i++ {
for range 100 {
id, err := newSessionID()
if err != nil {
t.Fatalf("newSessionID: %v", err)
+4 -8
View File
@@ -208,18 +208,14 @@ func (c *Conn) dispatch(ctx context.Context, line []byte) {
hasID := len(in.ID) > 0
switch {
case in.Method != "" && hasID:
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.wg.Go(func() {
c.serveRequest(ctx, in.ID, in.Method, in.Params)
}()
})
case in.Method != "" && !hasID:
if h := c.notH[in.Method]; h != nil {
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.wg.Go(func() {
h(ctx, in.Params)
}()
})
}
case in.Method == "" && hasID:
c.resolve(in)
+2 -3
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"log/slog"
"maps"
"os"
"path/filepath"
"sort"
@@ -2955,9 +2956,7 @@ func mapString(in map[string]string) map[string]string {
return nil
}
out := make(map[string]string, len(in))
for k, v := range in {
out[k] = v
}
maps.Copy(out, in)
return out
}
+1 -1
View File
@@ -411,7 +411,7 @@ func TestACPCtrlReadPathsDoNotRaceWithRebuild(t *testing.T) {
done := make(chan struct{})
go func() {
defer close(done)
for i := 0; i < rebuilds; i++ {
for i := range rebuilds {
if err := svc.rebuildSession(context.Background(), sess, SessionConfigState{Model: models[i%len(models)]}, []sessionConfigDelta{{axis: "model", model: models[i%len(models)]}}); err != nil {
t.Errorf("rebuildSession %d: %v", i, err)
return
+4 -3
View File
@@ -6,6 +6,7 @@ import (
"errors"
"math"
"runtime"
"slices"
"strings"
"sync"
@@ -710,9 +711,9 @@ func finalAssistantSummary(ctrl acpController) string {
return ""
}
history := ctrl.History()
for i := len(history) - 1; i >= 0; i-- {
if history[i].Role == provider.RoleAssistant && strings.TrimSpace(history[i].Content) != "" {
return history[i].Content
for _, v := range slices.Backward(history) {
if v.Role == provider.RoleAssistant && strings.TrimSpace(v.Content) != "" {
return v.Content
}
}
return ""
+4 -7
View File
@@ -1187,10 +1187,7 @@ func New(prov provider.Provider, tools *tool.Registry, session *Session, opts Op
} else {
maxSubagentDepth = NormalizeMaxSubagentDepth(maxSubagentDepth)
}
subagentDepth := opts.SubagentDepth
if subagentDepth < 0 {
subagentDepth = 0
}
subagentDepth := max(opts.SubagentDepth, 0)
reasoningByteLimit := opts.ReasoningByteLimit
if reasoningByteLimit == 0 {
reasoningByteLimit = defaultReasoningByteLimit
@@ -3297,7 +3294,7 @@ launch:
<-sem
break
}
i := i
wg.Add(1)
ranUntil = i + 1
go func() {
@@ -4016,8 +4013,8 @@ func (a *Agent) toolReadOnly(name string) bool {
// firstLine returns s up to its first newline — a one-line failure summary for
// the display Err, while the full error stays in the model-facing output.
func firstLine(s string) string {
if i := strings.IndexByte(s, '\n'); i >= 0 {
return s[:i]
if before, _, ok := strings.Cut(s, "\n"); ok {
return before
}
return s
}
+1 -4
View File
@@ -302,10 +302,7 @@ func MarkSessionInFlightTurn(sessionPath string, startMessageIndex int, preserve
// what lets crash recovery relocate the turn after an in-turn compaction has
// rewritten its original message index.
func SetSessionInFlightTurn(sessionPath string, marker InFlightTurnMeta) error {
startMessageIndex := marker.StartMessageIndex
if startMessageIndex < 0 {
startMessageIndex = 0
}
startMessageIndex := max(marker.StartMessageIndex, 0)
// The sidecar is read-modify-write; the per-path save lock keeps concurrent
// writers (autosave's UpdateSessionMeta, listing backfill) from dropping
// each other's fields.
+2 -2
View File
@@ -26,7 +26,7 @@ type PrefixShape struct {
// every call site, while still assigning to event.Event.CacheDiagnostics.
type CacheDiagnostics = event.CacheDiagnostics
func shortHash(v interface{}) string {
func shortHash(v any) string {
b, _ := json.Marshal(v)
h := sha256.Sum256(b)
return fmt.Sprintf("%x", h[:8])
@@ -39,7 +39,7 @@ func CaptureShape(systemPrompt string, schemas []provider.ToolSchema, rewriteVer
return PrefixShape{
SystemHash: shortHash(systemPrompt),
ToolsHash: shortHash(string(toolsJSON)),
PrefixHash: shortHash(map[string]interface{}{
PrefixHash: shortHash(map[string]any{
"system": systemPrompt,
"tools": string(toolsJSON),
}),
+5 -5
View File
@@ -199,7 +199,7 @@ func TestCacheHitClimbsWithoutCompaction(t *testing.T) {
a, sink := newAgent(t, srv.URL, mock.tools(), 0 /*no compaction*/, 0)
const turns = 14
for i := 0; i < turns; i++ {
for i := range turns {
userMsg := "Turn " + fmt.Sprint(i) + ": " + strings.Repeat("please consider this requirement. ", 6)
if err := a.Run(context.Background(), userMsg); err != nil {
t.Fatalf("Run %d: %v", i, err)
@@ -286,7 +286,7 @@ func TestReasoningRoundTripCost(t *testing.T) {
defer srv.Close()
a, sink := newAgent(t, srv.URL, mock.tools(), 0, 0)
const turns = 12
for i := 0; i < turns; i++ {
for i := range turns {
if err := a.Run(context.Background(), strings.Repeat("please consider this requirement. ", 6)); err != nil {
t.Fatalf("Run %d: %v", i, err)
}
@@ -329,7 +329,7 @@ func TestSessionAggregateCacheRate(t *testing.T) {
a, sink := newAgent(t, srv.URL, mock.tools(), 0, 0)
const turns = 8
for i := 0; i < turns; i++ {
for i := range turns {
if err := a.Run(context.Background(), strings.Repeat("please consider this requirement. ", 6)); err != nil {
t.Fatalf("Run %d: %v", i, err)
}
@@ -409,7 +409,7 @@ func TestReleaseCacheHitGuard(t *testing.T) {
name: "mixed-message-sizes",
run: func(t *testing.T) []int {
msgs := make([]string, 0, 20)
for i := 0; i < 20; i++ {
for i := range 20 {
repeats := 4
if i%3 == 2 {
repeats = 20
@@ -482,7 +482,7 @@ func cacheCurve(t *testing.T, mock *mockDeepSeek, turns int) []int {
func repeatedMessages(turns, repeats int) []string {
msgs := make([]string, 0, turns)
for i := 0; i < turns; i++ {
for i := range turns {
msgs = append(msgs, "Turn "+fmt.Sprint(i)+": "+strings.Repeat("please consider this requirement. ", repeats))
}
return msgs
+2 -2
View File
@@ -130,7 +130,7 @@ func (closedStreamProvider) Stream(context.Context, provider.Request) (<-chan pr
}
func TestCanceledContextClosedProviderStreamReturnsCancel(t *testing.T) {
for i := 0; i < 100; i++ {
for i := range 100 {
ctx, cancel := context.WithCancel(context.Background())
cancel()
@@ -574,7 +574,7 @@ func TestCancelInsideLargeParallelBatchStopsSchedulingNewTools(t *testing.T) {
reg.Add(trackingTool{name: "readonly_tracking", readOnly: true})
var calls []provider.ToolCall
for i := 0; i < 12; i++ {
for i := range 12 {
calls = append(calls, provider.ToolCall{
ID: fmt.Sprintf("call-%02d", i),
Name: "readonly_tracking",
+1 -1
View File
@@ -109,7 +109,7 @@ func compactionsPerTurn(t *testing.T, windowTok int, blob, finalText string, tur
})
perTurn = make([]int, turns)
for i := 0; i < turns; i++ {
for i := range turns {
before := started
if err := a.Run(context.Background(), fmt.Sprintf("turn %d: keep going, continue the work", i)); err != nil {
t.Fatalf("Run %d: %v", i, err)
+13 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"slices"
"strings"
"time"
@@ -521,17 +522,17 @@ func plannerPlanRequestsApproval(plan string) bool {
}
// Match per line so a nearby negation ("无需等待用户批准", "no need to wait
// for approval") exempts only its own phrase, not the whole plan.
for _, rawLine := range strings.Split(lower, "\n") {
for rawLine := range strings.SplitSeq(lower, "\n") {
line := strings.TrimSpace(rawLine)
if line == "" {
continue
}
for _, phrase := range plannerApprovalPhrases {
idx := strings.Index(line, phrase)
if idx < 0 {
before, _, ok := strings.Cut(line, phrase)
if !ok {
continue
}
if approvalMentionNegated(line[:idx]) {
if approvalMentionNegated(before) {
continue
}
return true
@@ -633,7 +634,7 @@ func parsePlannerAskBlock(plan string) (event.AskQuestion, bool) {
block := plan[start+len(plannerAskStartMarker) : end]
var question string
var options []event.AskOption
for _, raw := range strings.Split(block, "\n") {
for raw := range strings.SplitSeq(block, "\n") {
line := strings.TrimSpace(raw)
if line == "" {
continue
@@ -671,8 +672,8 @@ func parsePlannerAskBlock(plan string) (event.AskQuestion, bool) {
func plannerQuestionPrompt(plan string) string {
lines := strings.Split(plan, "\n")
for i := len(lines) - 1; i >= 0; i-- {
line := strings.TrimSpace(strings.Trim(lines[i], "-* \t"))
for _, v := range slices.Backward(lines) {
line := strings.TrimSpace(strings.Trim(v, "-* \t"))
if line == "" {
continue
}
@@ -780,8 +781,8 @@ func isNoOpPlan(plan string) bool {
func lastNonEmptyLine(s string) string {
lines := strings.Split(s, "\n")
for i := len(lines) - 1; i >= 0; i-- {
if t := strings.TrimSpace(lines[i]); t != "" {
for _, v := range slices.Backward(lines) {
if t := strings.TrimSpace(v); t != "" {
return t
}
}
@@ -1076,11 +1077,11 @@ func HandoffTask(s string) string {
return s
}
const header = "Original task:\n"
i := strings.Index(trimmed, header)
if i < 0 {
_, after, ok := strings.Cut(trimmed, header)
if !ok {
return s
}
rest := trimmed[i+len(header):]
rest := after
if j := strings.Index(rest, "\n\nPlanner output:"); j >= 0 {
rest = rest[:j]
}
+9 -13
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"reasonix/internal/event"
"slices"
"strings"
"testing"
@@ -43,9 +44,9 @@ func (m *mockProvider) Stream(ctx context.Context, req provider.Request) (<-chan
}
func lastUser(req provider.Request) string {
for i := len(req.Messages) - 1; i >= 0; i-- {
if req.Messages[i].Role == provider.RoleUser {
return req.Messages[i].Content
for _, v := range slices.Backward(req.Messages) {
if v.Role == provider.RoleUser {
return v.Content
}
}
return ""
@@ -1274,17 +1275,12 @@ func toolSchemaNames(schemas []provider.ToolSchema) []string {
}
func contains(items []string, want string) bool {
for _, item := range items {
if item == want {
return true
}
}
return false
return slices.Contains(items, want)
}
func BenchmarkPlannerToolRegistry(b *testing.B) {
parentReg := tool.NewRegistry()
for i := 0; i < 200; i++ {
for i := range 200 {
parentReg.Add(coordinatorTestTool{
name: fmt.Sprintf("tool_%03d", i),
readOnly: i%3 != 0,
@@ -1294,7 +1290,7 @@ func BenchmarkPlannerToolRegistry(b *testing.B) {
parentReg.Add(coordinatorTestTool{name: "write_file", readOnly: false})
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for range b.N {
reg := PlannerToolRegistry(parentReg)
if reg.Len() == 0 {
b.Fatal("planner registry should retain read-only research tools")
@@ -1727,7 +1723,7 @@ func TestCoordinatorHandoffSurvivesPlannerCompaction(t *testing.T) {
// below) its pre-turn length, which is what strands a boundary based on
// the pre-turn message count.
filler := strings.Repeat("planner history filler. ", 150)
for i := 0; i < 3; i++ {
for range 3 {
plannerSess.Add(provider.Message{Role: provider.RoleUser, Content: filler})
plannerSess.Add(provider.Message{Role: provider.RoleAssistant, Content: filler})
}
@@ -1873,7 +1869,7 @@ func TestCoordinatorFailedTurnRollbackKeepsCompaction(t *testing.T) {
executor := New(exec, tool.NewRegistry(), NewSession("exec-sys"), Options{}, event.Discard)
plannerSess := NewSession("planner-sys")
filler := strings.Repeat("planner history filler. ", 150)
for i := 0; i < 3; i++ {
for range 3 {
plannerSess.Add(provider.Message{Role: provider.RoleUser, Content: filler})
plannerSess.Add(provider.Message{Role: provider.RoleAssistant, Content: filler})
}
+1 -1
View File
@@ -180,7 +180,7 @@ func BenchmarkHasVisibleFinalAnswer(b *testing.B) {
b.Run(tc.name, func(b *testing.B) {
b.ReportAllocs()
var got bool
for i := 0; i < b.N; i++ {
for range b.N {
got = hasVisibleFinalAnswer(tc.text)
}
_ = got
+2 -4
View File
@@ -323,9 +323,7 @@ func (f *FleetTool) runFleet(ctx context.Context, sink event.Sink, specs []Profi
},
})
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
// Each fleet item runs as its own task-shaped execution so
// transcripts, evidence, and scheduler claims stay independent.
itemCtx := withCallContext(ctx, subID, subSinkFor(subID, sink), nil, false)
@@ -350,7 +348,7 @@ func (f *FleetTool) runFleet(ctx context.Context, sink event.Sink, specs []Profi
})
}
doneCh <- res
}()
})
}
// Mark only genuinely unstarted items skipped. Started items always publish
+1 -1
View File
@@ -356,7 +356,7 @@ func TestFleetParallelDisjointWriters(t *testing.T) {
f := NewFleetTool(task)
tasks := make([]map[string]any, 0, 4)
for i := 0; i < 4; i++ {
for i := range 4 {
path := filepath.Join("docs", "f"+string(rune('0'+i))+".md")
tasks = append(tasks, map[string]any{
"prompt": "handle " + path,
+3 -3
View File
@@ -56,11 +56,11 @@ func StripAutoResearchEvidenceBlocks(text string) string {
}
b.WriteString(rest[:start])
afterOpen := rest[start+len(autoResearchEvidenceOpen):]
end := strings.Index(afterOpen, autoResearchEvidenceClose)
if end < 0 {
_, after, ok := strings.Cut(afterOpen, autoResearchEvidenceClose)
if !ok {
return strings.TrimSpace(b.String())
}
rest = afterOpen[end+len(autoResearchEvidenceClose):]
rest = after
}
}
+3 -2
View File
@@ -3,6 +3,7 @@ package agent
import (
"fmt"
"html"
"slices"
"strings"
"reasonix/internal/provider"
@@ -24,8 +25,8 @@ func (a *Agent) pendingInterruptedRecovery() *provider.InterruptedTurnRecovery {
return nil
}
msgs := a.session.Snapshot()
for i := len(msgs) - 1; i >= 0; i-- {
m := msgs[i]
for _, v := range slices.Backward(msgs) {
m := v
if m.LocalOnly && m.InterruptedTurn != nil && m.InterruptedTurn.Pending {
copy := *m.InterruptedTurn
copy.CompletedTools = append([]provider.InterruptedToolSummary(nil), copy.CompletedTools...)
+2 -2
View File
@@ -417,7 +417,7 @@ func TestRunStreamRetryRequestCountIsLinearNotTriangular(t *testing.T) {
func TestRunExhaustedStreamRetriesPersistPendingLocalOnly(t *testing.T) {
interrupted := &provider.StreamInterruptedError{Err: errors.New("eof"), Reason: provider.StreamInterruptPrematureEOF}
turns := make([]testutil.Turn, 0, maxSamplingAttempts)
for i := 0; i < maxSamplingAttempts; i++ {
for range maxSamplingAttempts {
turns = append(turns, testutil.Turn{Text: "half", ChunkError: interrupted})
}
mp := testutil.NewMock("m", turns...)
@@ -1026,7 +1026,7 @@ func TestHealthyToolCallReasoningRetriesTransientStateWriteFailure(t *testing.T)
t.Fatal(err)
}
permissionsRestored = true
for healthy := 0; healthy < missingReasoningHealthyResolveStreak-1; healthy++ {
for healthy := range missingReasoningHealthyResolveStreak - 1 {
if missing, retry := a.observeMissingToolCallReasoning(calls, "healthy reasoning"); missing || retry {
t.Fatalf("healthy recovery observation %d = missing:%v retry:%v, want false/false", healthy+1, missing, retry)
}
+5 -8
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"strings"
"sync"
@@ -177,9 +178,7 @@ func (p *ParallelTasksTool) Execute(ctx context.Context, args json.RawMessage) (
},
})
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
modelRef, effortRef := p.taskTool.effectiveProfile(t.Model, t.Effort)
itemCtx := withCallContext(ctx, subID, subSinkFor(subID, sink), nil, PlanModeFromContext(ctx))
// Route through TaskTool's unified runner so persisted parent sessions
@@ -221,7 +220,7 @@ func (p *ParallelTasksTool) Execute(ctx context.Context, args json.RawMessage) (
})
answer, ref := splitSubagentRunResult(output)
doneCh <- subResult{index: idx, output: answer, ref: ref}
}()
})
}
markCancelled := func(err error) {
@@ -300,10 +299,8 @@ func parallelGroupTerminalPhase(ctx context.Context, err error, statuses []paral
if ctx.Err() != nil {
return subagentPhaseCancelled
}
for _, st := range statuses {
if st == parallelTaskFailed {
return subagentPhaseFailed
}
if slices.Contains(statuses, parallelTaskFailed) {
return subagentPhaseFailed
}
if err != nil {
return subagentPhaseFailed
+3 -3
View File
@@ -377,9 +377,9 @@ func (parallelLongResultProvider) Stream(_ context.Context, req provider.Request
func subagentRefsFromText(text string) []string {
var refs []string
for _, line := range strings.Split(text, "\n") {
if strings.HasPrefix(line, "Subagent reference: ") {
refs = append(refs, strings.TrimSpace(strings.TrimPrefix(line, "Subagent reference: ")))
for line := range strings.SplitSeq(text, "\n") {
if after, ok := strings.CutPrefix(line, "Subagent reference: "); ok {
refs = append(refs, strings.TrimSpace(after))
}
}
return refs
+3 -3
View File
@@ -98,7 +98,7 @@ func TestPruneNeverRewritesLocalInterruptedDisplay(t *testing.T) {
func TestSnipStaleToolResults(t *testing.T) {
var lines []string
for i := 0; i < 1000; i++ {
for range 1000 {
lines = append(lines, "line")
}
big := strings.Join(lines, "\n")
@@ -376,7 +376,7 @@ func TestSnipUsesRegisteredToolHint(t *testing.T) {
// 600 numbered lines; a SnipHinter keeping head=3, tail=2 must yield exactly
// those boundary lines, which no default geometry would produce.
var lines []string
for i := 0; i < 600; i++ {
for i := range 600 {
lines = append(lines, fmt.Sprintf("L%d", i))
}
content := strings.Join(lines, "\n")
@@ -403,7 +403,7 @@ func TestSnipUsesRegisteredToolHint(t *testing.T) {
func TestSnipFallsBackByReadOnlyTier(t *testing.T) {
var lines []string
for i := 0; i < 600; i++ {
for i := range 600 {
lines = append(lines, fmt.Sprintf("L%d", i))
}
content := strings.Join(lines, "\n")
+3 -3
View File
@@ -242,11 +242,11 @@ func hasOpenTag(s, tag string) bool {
func trimLeadingTransientBlock(content, tag string) (string, bool) {
closeTag := "</" + tag + ">"
i := strings.Index(content, closeTag)
if i < 0 {
_, after, ok := strings.Cut(content, closeTag)
if !ok {
return content, false
}
return strings.TrimLeft(content[i+len(closeTag):], " \t\r\n"), true
return strings.TrimLeft(after, " \t\r\n"), true
}
// WithResponseLanguagePreference carries the runtime final-answer language
+1 -7
View File
@@ -214,13 +214,7 @@ func normalizeMissingReasoningIncident(incident missingReasoningIncident, now ti
}
func (incident missingReasoningIncident) lastEventUnixNano() int64 {
lastEvent := incident.LastMissingUnixNano
if incident.LastResolvedAtUnixNano > lastEvent {
lastEvent = incident.LastResolvedAtUnixNano
}
if incident.LastHealthyAtUnixNano > lastEvent {
lastEvent = incident.LastHealthyAtUnixNano
}
lastEvent := max(incident.LastHealthyAtUnixNano, max(incident.LastResolvedAtUnixNano, incident.LastMissingUnixNano))
return lastEvent
}
+4 -8
View File
@@ -406,14 +406,12 @@ func TestMissingReasoningWarnStateConcurrentSameIncidentWarnsOnce(t *testing.T)
var warned atomic.Int64
var wg sync.WaitGroup
for range 8 {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
<-start
if newMissingReasoningWarnState(dir).claimAt(fingerprint, now) {
warned.Add(1)
}
}()
})
}
close(start)
wg.Wait()
@@ -490,14 +488,12 @@ func TestMissingReasoningWarnStateConcurrentClaimsKeepEveryConfiguration(t *test
var wg sync.WaitGroup
for _, label := range labels {
fingerprint := warningFingerprint(label)
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
<-start
if !newMissingReasoningWarnState(dir).claimAt(fingerprint, now) {
t.Errorf("fresh configuration %q did not claim its notice", label)
}
}()
})
}
close(start)
wg.Wait()
+2 -2
View File
@@ -457,7 +457,7 @@ func publishRecoveryTrashStage(dir, key, stageDir string) (string, error) {
root := filepath.Join(dir, recoveryTrashDir)
stem := strings.TrimSuffix(key, filepath.Ext(key))
stamp := time.Now().UTC().UnixMilli()
for i := 0; i < 1000; i++ {
for i := range 1000 {
name := key
if i > 0 {
name = fmt.Sprintf("%s-recovery-%d-%d", stem, stamp, i)
@@ -523,7 +523,7 @@ func reserveRecoveryTrashItemDir(dir, key string) (string, string, error) {
return "", "", err
}
stem := strings.TrimSuffix(key, filepath.Ext(key))
for i := 0; i < 1000; i++ {
for i := range 1000 {
name := key
if i > 0 {
name = fmt.Sprintf("%s-recovery-%d-%d", stem, time.Now().UTC().UnixMilli(), i)
+2 -2
View File
@@ -303,7 +303,7 @@ func TestRepeatGuardRetainsStaleEditFailuresAcrossGoalScope(t *testing.T) {
TaskText: "fix prompt.txt",
})
for i := 0; i < 3; i++ {
for i := range 3 {
if err := a.Run(ctx, "continue goal"); err != nil {
t.Fatalf("Run %d: %v", i+1, err)
}
@@ -335,7 +335,7 @@ func TestRepeatGuardClearsOrdinaryWriteFailureAcrossGoalRuns(t *testing.T) {
TaskText: "fix prompt.txt",
})
for i := 0; i < 3; i++ {
for i := range 3 {
if err := a.Run(ctx, "continue goal"); err != nil {
t.Fatalf("Run %d: %v", i+1, err)
}
+2 -11
View File
@@ -435,10 +435,7 @@ func (a *Agent) streamWithSamplingRecovery(ctx context.Context, turn int) stream
before := provider.RequestAttemptCount(ctx)
result := a.streamWithFrozen(ctx, turn, sink, &frozen, attemptID)
after := provider.RequestAttemptCount(ctx)
delta := after - before
if delta < 0 {
delta = 0
}
delta := max(after-before, 0)
// httpRequests=0 means the provider does not use SendWithRetry
// (extension/custom), or it failed before issuing an HTTP request.
// Only overwrite RequestCount when the built-in counter observed POSTs;
@@ -581,13 +578,7 @@ var streamRetrySleep = sleepStreamRetryBackoff
// Returns false when ctx is cancelled during the wait.
func sleepStreamRetryBackoff(ctx context.Context, attempt int) bool {
// attempt is 1-based for the failed attempt about to be retried.
shift := attempt - 1
if shift < 0 {
shift = 0
}
if shift > 4 {
shift = 4
}
shift := min(max(attempt-1, 0), 4)
base := time.Duration(1<<shift) * 500 * time.Millisecond
jitter := time.Duration(rand.Intn(250)) * time.Millisecond
timer := time.NewTimer(base + jitter)
+7 -8
View File
@@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"runtime"
"slices"
"sort"
"strings"
"sync"
@@ -679,14 +680,12 @@ func (s *Session) saveRecoveryBranch(opts RecoveryBranchOptions, shutdown bool)
return RecoveryBranchInfo{}, fmt.Errorf("%w: %s is already %d recovery forks deep",
ErrSessionRecoveryDepthExceeded, originalPath, parentDepth)
}
recoveryDepth := parentDepth + 1
if recoveryDepth > SessionRecoveryMaxDepth {
recoveryDepth := min(parentDepth+1,
// A shutdown copy is allowed even when the ordinary conflict chain is
// capped because losing the only in-memory transcript is worse than one
// additional branch. Keep the saturated depth so later ordinary saves
// still enforce the existing anti-cascade policy.
recoveryDepth = SessionRecoveryMaxDepth
}
SessionRecoveryMaxDepth)
recoveryPath := recoverySessionPath(originalPath, digest)
if shutdown {
@@ -808,8 +807,8 @@ func recoveryParentStem(parent string) string {
return "session"
}
sum := sha256.Sum256([]byte(parent))
if idx := strings.Index(parent, "-recovery-"); idx >= 0 {
base := strings.Trim(parent[:idx], "-_. ")
if before, _, ok := strings.Cut(parent, "-recovery-"); ok {
base := strings.Trim(before, "-_. ")
if base == "" {
base = "session"
}
@@ -1265,8 +1264,8 @@ func resolvePathThroughExistingAncestor(path string) string {
missing := make([]string, 0, 4)
for {
if resolved, err := filepath.EvalSymlinks(current); err == nil {
for i := len(missing) - 1; i >= 0; i-- {
resolved = filepath.Join(resolved, missing[i])
for _, v := range slices.Backward(missing) {
resolved = filepath.Join(resolved, v)
}
return resolved
}
+1 -1
View File
@@ -2117,7 +2117,7 @@ func TestSaveSnapshotSameRevisionAllowsOwnedNonPrefixAppend(t *testing.T) {
// Simulate recovery: load the saved transcript into a new session, then
// add more messages. Every subsequent SaveSnapshot must succeed without a
// diverged conflict while the persisted digest still proves ownership.
for turn := 0; turn < 5; turn++ {
for turn := range 5 {
s, err := LoadSession(path)
if err != nil {
t.Fatalf("LoadSession turn %d: %v", turn, err)
+3 -5
View File
@@ -16,10 +16,8 @@ func TestSchedulerTotalConcurrencyQueues(t *testing.T) {
var wg sync.WaitGroup
barrier := make(chan struct{})
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 4 {
wg.Go(func() {
release, err := s.Acquire(context.Background(), AcquireRequest{Writer: false})
if err != nil {
t.Errorf("acquire: %v", err)
@@ -35,7 +33,7 @@ func TestSchedulerTotalConcurrencyQueues(t *testing.T) {
<-barrier
started.Add(-1)
release()
}()
})
}
// Wait until at least 2 are running, then release them.
Loaded 100 of 428 files, more files were not shown because too many files have changed in this diff. Show more