]> git.giacomolli.uk - onechat/commitdiff
Log failed summary builds
authorAnderson A. Giacomolli <redacted>
Mon, 9 Nov 2026 13:35:37 +0000 (10:35 -0300)
committerAnderson A. Giacomolli <redacted>
Mon, 9 Nov 2026 13:35:37 +0000 (10:35 -0300)
A build of a summary node that fails logs one line with the node and the
cause, and tells that the build is tried again at the next message. A
build that a stop of the daemon ends logs nothing. The error of the
compactor holds no node, because the line names it once.

The tests give each memory a logger, and a shared helper gives the ones
that the tests do not read.

Issue: e4acfcb

17 files changed:
chain_test.go
compactor.go
compactor_test.go
daemon.go
daemon_start_helper_test.go
discard_log_helper_test.go [new file with mode: 0644]
memory.go
memory_test.go
probe.go
probe_test.go
replay_test.go
schedule_helper_test.go
schedule_test.go
subagent_run_test.go
syncer_test.go
tools_test.go
turn_test.go

index 070a5f2a6f4318538b74b538aa3e2328d5b56f63..55176202b66063b1c9838589edb22041777957dc 100644 (file)
@@ -26,7 +26,7 @@ func TestChainReturnsTheRepliesOfTheFinalAnswer(t *testing.T) {
                }, 120, 100, 5))
        }
 
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        t.Cleanup(func() {
index e7a68664d8b138f0e3cda1dd391afccf1093845c..efb183ff583b272c49430c810cd3ca4d9ee8df3a 100644 (file)
@@ -65,7 +65,7 @@ func (c *Compactor) Summarize(ctx context.Context, task Task) (string, error) {
 
        instructions, err := c.instructions()
        if err != nil {
-               return "", fmt.Errorf("compact node (%d, %d): %w", task.Level, task.Index, err)
+               return "", err
        }
 
        var first []any
@@ -109,7 +109,7 @@ func (c *Compactor) Summarize(ctx context.Context, task Task) (string, error) {
        }
 
        if len(lines) == 0 {
-               return "", fmt.Errorf("compact node (%d, %d): %w", task.Level, task.Index, err)
+               return "", err
        }
 
        return compactorShortest(lines), nil
index 0d591ee9e5602135ef113712e405bddbe3c11bb8..0cab0d0bf15c8553097205369a19c2cbdf3a4b35 100644 (file)
@@ -255,7 +255,7 @@ func TestCompactorBuildsNodesInTheCore(t *testing.T) {
                modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("summary of the long output")}, 100, 0, 5))
        }
 
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newTestCompactor(t, server))
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newTestCompactor(t, server), discardLog())
        require.NoError(t, err)
 
        defer func() {
index fb7f5c4dd983dc71be428204f5ecec94ba4c78e5..c2cf33b76c42d29cad022441c1f6c366bc7cce6b 100644 (file)
--- a/daemon.go
+++ b/daemon.go
@@ -86,7 +86,7 @@ func daemon(ctx context.Context, out io.Writer) (err error) {
        }
 
        client := newModelClient(address, auth, agent.CacheKey(), filepath.Join(agent.dir, "usage.jsonl"))
-       memory, err := openMemory(ctx, agent.dir, time.Now, newCompactor(client, agent.Prompt, config.Compactor))
+       memory, err := openMemory(ctx, agent.dir, time.Now, newCompactor(client, agent.Prompt, config.Compactor), logger)
        if err != nil {
                return err
        }
index 0c25acc01dff4b8fa93cddd9c43f7d799d3a4a6e..8ed36e990e2f9b34121f6870b7053af66ce324fc 100644 (file)
@@ -2,8 +2,6 @@ package main
 
 import (
        "context"
-       "io"
-       "log"
        "path/filepath"
        "testing"
        "time"
@@ -42,7 +40,7 @@ func daemonStartTestWithSchedules(t *testing.T, server *ModelFake, bot *Telegram
        require.NoError(t, err)
 
        agent.config.Workdir = t.TempDir()
-       memory, err := openMemory(t.Context(), agent.dir, clock, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), agent.dir, clock, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        socket := filepath.Join(dir, socketFileName)
@@ -54,7 +52,7 @@ func daemonStartTestWithSchedules(t *testing.T, server *ModelFake, bot *Telegram
        done := make(chan error)
        go func() {
                done <- daemonServe(ctx, listener, bot, whatsapp, schedules, func(notify func(Event)) (*TurnLoop, error) {
-                       return newTurnLoop(agent, memory, newTestSubagents(t), whatsapp, schedules, client, clock, log.New(io.Discard, "", 0), notify)
+                       return newTurnLoop(agent, memory, newTestSubagents(t), whatsapp, schedules, client, clock, discardLog(), notify)
                })
        }()
 
diff --git a/discard_log_helper_test.go b/discard_log_helper_test.go
new file mode 100644 (file)
index 0000000..9c24880
--- /dev/null
@@ -0,0 +1,11 @@
+package main
+
+import (
+       "io"
+       "log"
+)
+
+// discardLog returns a logger whose lines a test does not read.
+func discardLog() *log.Logger {
+       return log.New(io.Discard, "", 0)
+}
index 0fd7d835f584bdea877f09bc063aebe969ddf1fa..751a5dc2ba205c3fa6684fc02f13412e933f24a2 100644 (file)
--- a/memory.go
+++ b/memory.go
@@ -5,6 +5,7 @@ import (
        "encoding/json"
        "errors"
        "fmt"
+       "log"
        "os"
        "path/filepath"
        "strings"
@@ -22,9 +23,9 @@ var (
 )
 
 // openMemory opens the chat in root and starts the builds that are ready. The
-// clock gives the dates of new messages and the days of new nodes. The builds
-// stop when ctx ends or at Close.
-func openMemory(ctx context.Context, root string, clock func() time.Time, summarizer Summarizer) (*Memory, error) {
+// clock gives the dates of new messages and the days of new nodes. logger gets
+// one line for each failed build. The builds stop when ctx ends or at Close.
+func openMemory(ctx context.Context, root string, clock func() time.Time, summarizer Summarizer, logger *log.Logger) (*Memory, error) {
        log, err := openLog(ctx, root, clock)
        if err != nil {
                return nil, err
@@ -35,6 +36,7 @@ func openMemory(ctx context.Context, root string, clock func() time.Time, summar
                log:        log,
                queue:      newBuildQueue(),
                summarizer: summarizer,
+               logger:     logger,
                changed:    make(chan struct{}),
        }
 
@@ -106,6 +108,7 @@ type Memory struct {
        cview      *View
        queue      *BuildQueue
        summarizer Summarizer
+       logger     *log.Logger
        ctx        context.Context
        cancel     context.CancelFunc
        workers    sync.WaitGroup
@@ -394,7 +397,9 @@ func (m *Memory) dispatch() {
 
                task, err := m.task(key)
                if err != nil {
+                       m.failed(key, fmt.Errorf("read its input: %w", err))
                        m.queue.Fail(key)
+
                        continue
                }
 
@@ -561,6 +566,10 @@ func (m *Memory) run(key NodeKey, task Task) {
                m.syncer.Written()
        }
 
+       if err != nil && m.ctx.Err() == nil {
+               m.failed(key, err)
+       }
+
        if err != nil {
                m.queue.Fail(key)
        } else {
@@ -572,6 +581,11 @@ func (m *Memory) run(key NodeKey, task Task) {
        m.dispatch()
 }
 
+// failed logs the failed build of the node key with its cause err.
+func (m *Memory) failed(key NodeKey, err error) {
+       m.logger.Printf("a summary of the chat failed, and it is tried again at the next message: node (%d, %d): %v", key.level, key.index, err)
+}
+
 // ready tells if both children of the node key are built and the node is not.
 func (m *Memory) ready(key NodeKey) (bool, error) {
        for _, index := range []int{2 * key.index, 2*key.index + 1} {
index 4dee8b2951e18d2cb6d47ca60a0414e02054545d..ee932d1f36387b03132569f7f2b047b3b5f4f4e9 100644 (file)
@@ -3,6 +3,7 @@ package main
 import (
        "context"
        "fmt"
+       "log"
        "maps"
        "os"
        "path/filepath"
@@ -17,7 +18,7 @@ import (
 func TestMemoryRestartLoadsTheSameView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        memory.chat.high = 200
@@ -36,7 +37,7 @@ func TestMemoryRestartLoadsTheSameView(t *testing.T) {
        require.Less(t, len(memory.chat.lines), 100)
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -54,7 +55,7 @@ func TestMemoryRestartLoadsTheSameView(t *testing.T) {
 func TestMemoryOpensAfterACrashBeforeTheViewSave(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "first")
@@ -72,14 +73,14 @@ func TestMemoryOpensAfterACrashBeforeTheViewSave(t *testing.T) {
        // The log and the tree hold message 1, but the view does not.
        require.NoError(t, os.WriteFile(filepath.Join(root, "view.json"), saved, 0o600))
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
        require.Equal(t, view, memory.View())
        require.NoError(t, memory.Close())
 }
 
 func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -110,7 +111,7 @@ func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
 func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.gate = make(chan struct{})
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -148,7 +149,7 @@ func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
 func TestMemorySettleWaitsForThePendingLines(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.gate = make(chan struct{})
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -178,7 +179,8 @@ func TestMemorySettleWaitsForThePendingLines(t *testing.T) {
 func TestMemorySettleFailsWhileABuildFailed(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       logs := &SyncBuffer{}
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, log.New(logs, "", 0))
        require.NoError(t, err)
 
        defer func() {
@@ -188,6 +190,7 @@ func TestMemorySettleFailsWhileABuildFailed(t *testing.T) {
        _, err = memory.Append("echo", memoryLongText(0))
        require.NoError(t, err)
        require.ErrorIs(t, memory.Settle(t.Context()), errMemoryUnsettled)
+       require.Equal(t, "a summary of the chat failed, and it is tried again at the next message: node (0, 0): fake failure\n", logs.String())
 
        summarizer.SetFailing(false)
        _, err = memory.Append("user", "retry")
@@ -198,7 +201,7 @@ func TestMemorySettleFailsWhileABuildFailed(t *testing.T) {
 func TestMemorySettleEndsWithItsContext(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.gate = make(chan struct{})
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -217,7 +220,7 @@ func TestMemorySettleEndsWithItsContext(t *testing.T) {
 func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -246,7 +249,7 @@ func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
 
 func TestMemoryMergesAfterBothHalves(t *testing.T) {
        summarizer := newFakeSummarizer()
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -275,7 +278,7 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
        clock := newTestClock()
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), root, clock.Now, summarizer)
+       memory, err := openMemory(t.Context(), root, clock.Now, summarizer, discardLog())
        require.NoError(t, err)
 
        for n := range 4 {
@@ -286,7 +289,7 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
        require.NoError(t, memory.Wait(t.Context()))
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -298,7 +301,7 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
 }
 
 func TestMemoryBuildsFreeParents(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -317,7 +320,7 @@ func TestMemoryBuildsFreeParents(t *testing.T) {
 func TestMemoryKeepsALongMessageOutOfTheView(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -343,7 +346,7 @@ func TestMemoryKeepsALongMessageOutOfTheView(t *testing.T) {
 func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
@@ -353,7 +356,7 @@ func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
        require.NoError(t, err)
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -366,7 +369,7 @@ func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
 func TestMemoryRefusesToRebuildAMissingView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
@@ -374,7 +377,7 @@ func TestMemoryRefusesToRebuildAMissingView(t *testing.T) {
        require.NoError(t, memory.Close())
        require.NoError(t, os.Remove(filepath.Join(root, "view.json")))
 
-       _, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       _, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.ErrorIs(t, err, errMemoryNoView)
 }
 
index ba656ffa536d971e70f3d3d160861b0fdbcf3296..5b233ed4455cbbec82739d6e456f0da7c4eba1d3 100644 (file)
--- a/probe.go
+++ b/probe.go
@@ -8,6 +8,7 @@ import (
        "errors"
        "fmt"
        "io"
+       "log"
        "maps"
        "os"
        "path/filepath"
@@ -105,7 +106,9 @@ func probe(ctx context.Context, args []string, out io.Writer) (err error) {
                slots: make(chan struct{}, builds),
        }
 
-       memory, err := openMemory(ctx, root, time.Now, summarizer)
+       // The report of the probe counts the failed calls, so the lines of the
+       // failed builds are not needed.
+       memory, err := openMemory(ctx, root, time.Now, summarizer, log.New(io.Discard, "", 0))
        if err != nil {
                return err
        }
index bd881a05ca3c1d53c7562cf29e813c2619e9aa41..1e07fd5fb8962acdb354edb3448953553aba9c0e 100644 (file)
@@ -419,7 +419,7 @@ func newTestProbeRun(t *testing.T, server *ModelFake) *ProbeRun {
        root := t.TempDir()
        usagePath := filepath.Join(root, "usage.jsonl")
        client := newModelClient(server.URL, ModelAPIKey(modelTestKey), "onechat-"+probeName, usagePath)
-       memory, err := openMemory(t.Context(), root, newTestClock().Now, newCompactor(client, probePrompt, defaultConfig().Compactor))
+       memory, err := openMemory(t.Context(), root, newTestClock().Now, newCompactor(client, probePrompt, defaultConfig().Compactor), discardLog())
        require.NoError(t, err)
 
        return &ProbeRun{
index dfbe6157a78014be1036fbd9a655ae258f2c3e8c..96091c2c9dde431fa12367f950bd34d6ff015f16 100644 (file)
@@ -38,7 +38,7 @@ func TestReplaySyntheticLog(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
        random := rand.New(rand.NewPCG(1, 2))
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -98,7 +98,7 @@ func replayRequireRestart(t *testing.T, memory *Memory, root string, clock *Test
        require.NoError(t, err)
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
        require.NoError(t, memory.Wait(t.Context()))
        require.Equal(t, view, memory.View())
index ea02ed6a0624f3fada5dc834c9462fc36ae937a5..814056cc037fbfaf149577c64bc5e133944e28d4 100644 (file)
@@ -1,8 +1,6 @@
 package main
 
 import (
-       "io"
-       "log"
        "path/filepath"
        "testing"
        "time"
@@ -14,7 +12,7 @@ import (
 func newTestSchedules(t *testing.T, clock func() time.Time) *Schedules {
        t.Helper()
 
-       schedules, err := openSchedules(filepath.Join(t.TempDir(), scheduleFileName), clock, log.New(io.Discard, "", 0))
+       schedules, err := openSchedules(filepath.Join(t.TempDir(), scheduleFileName), clock, discardLog())
        require.NoError(t, err)
 
        return schedules
index 85e11d09c12d0ca7786ab0d55b39b3b45485290d..96063aacae7611b4d8655473c8cff7b6ef3b2bea 100644 (file)
@@ -2,7 +2,6 @@ package main
 
 import (
        "context"
-       "io"
        "log"
        "os"
        "path/filepath"
@@ -130,7 +129,7 @@ func TestSchedulesSurviveARestart(t *testing.T) {
        require.NoError(t, err)
        require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
 
-       reopened, err := openSchedules(path, schedules.clock, log.New(io.Discard, "", 0))
+       reopened, err := openSchedules(path, schedules.clock, discardLog())
        require.NoError(t, err)
        require.Equal(t, "2 | Monday 2026-11-09 08:00 -03:00 | 0 8 * * 1-5 | Tell the user the news.", reopened.List())
 
@@ -232,7 +231,7 @@ func TestOpenSchedulesRefusesABadCron(t *testing.T) {
        path := filepath.Join(t.TempDir(), scheduleFileName)
        require.NoError(t, os.WriteFile(path, []byte(`{"next":1,"schedules":[{"id":1,"text":"x","cron":"* * * * *","due":"2026-11-09T09:00:00-03:00"}]}`), 0o600))
 
-       _, err := openSchedules(path, time.Now, log.New(io.Discard, "", 0))
+       _, err := openSchedules(path, time.Now, discardLog())
        require.ErrorContains(t, err, "schedule 1: the runs of")
 }
 
@@ -293,7 +292,7 @@ func TestDaemonRunsADueSchedule(t *testing.T) {
                now: time.Date(2026, time.November, 8, 20, 0, 0, 0, time.FixedZone("", -3*60*60)),
        }
 
-       schedules, err := openSchedules(filepath.Join(t.TempDir(), scheduleFileName), clock.Now, log.New(io.Discard, "", 0))
+       schedules, err := openSchedules(filepath.Join(t.TempDir(), scheduleFileName), clock.Now, discardLog())
        require.NoError(t, err)
 
        schedules.check = time.Millisecond
@@ -360,7 +359,7 @@ func scheduleTestOpen(t *testing.T) (*Schedules, string) {
                now: time.Date(2026, time.November, 8, 20, 0, 0, 0, time.FixedZone("", -3*60*60)),
        }
 
-       schedules, err := openSchedules(path, clock.Now, log.New(io.Discard, "", 0))
+       schedules, err := openSchedules(path, clock.Now, discardLog())
        require.NoError(t, err)
 
        return schedules, path
index a59b499eae015a037baf3daee59dce13cfda8a28..d784b8968431989989ead0ceba2e8a8b69de7c73 100644 (file)
@@ -3,7 +3,6 @@ package main
 import (
        "context"
        "io"
-       "log"
        "net/http"
        "os"
        "path/filepath"
@@ -515,13 +514,13 @@ func subagentTestStart(t *testing.T, agent *Agent, server *ModelFake, events *Tu
 
        agent.config.Workdir = t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), agent.dir, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), agent.dir, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        subagents := newSubagents(filepath.Join(agent.dir, subagentDirName), clock.Now, subagentNameRandom)
        client := newModelClient(server.URL, ModelAPIKey(modelTestKey), agent.CacheKey(), filepath.Join(agent.dir, "usage.jsonl"))
        whatsapp, _, _ := whatsappStartTest(t, t.TempDir(), clock)
-       loop, err := newTurnLoop(agent, memory, subagents, whatsapp, newTestSchedules(t, clock.Now), client, clock.Now, log.New(io.Discard, "", 0), events.Add)
+       loop, err := newTurnLoop(agent, memory, subagents, whatsapp, newTestSchedules(t, clock.Now), client, clock.Now, discardLog(), events.Add)
        require.NoError(t, err)
 
        ctx, cancel := context.WithCancel(t.Context())
index 09b2bb46f81481d4291e9c42d7985409e1b915c0..efa174ab3a21cd43a4836bc64ab1a7a31e124115 100644 (file)
@@ -86,7 +86,7 @@ func TestAFailedSyncFailsEachLaterFlush(t *testing.T) {
 }
 
 func TestMemoryFlushSyncsTheChat(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("tool", "shell: ls")
@@ -100,7 +100,7 @@ func TestKilledWriterLosesNoLoggedMessage(t *testing.T) {
        for round := range 3 {
                acknowledged := syncerRunKilledWriter(t, root, 200+100*round)
 
-               memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer())
+               memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer(), discardLog())
                require.NoError(t, err)
                require.GreaterOrEqual(t, memory.log.Len(), acknowledged)
                for id := range memory.log.Len() {
@@ -124,7 +124,7 @@ func TestWriterChild(t *testing.T) {
                t.Skip("runs only as a child process")
        }
 
-       memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, time.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        for id := memory.log.Len(); ; id++ {
index dc08fdc7083bef07f4a0033f56be74458adaf6f6..3ab784551cc56b76f7071a9372279078c8eea14d 100644 (file)
@@ -113,7 +113,7 @@ func TestFileTools(t *testing.T) {
 }
 
 func TestZoomAndDateReadTheChat(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
@@ -258,7 +258,7 @@ func (r *ToolRecordingLog) Events() []string {
 func newTestToolbox(t *testing.T, log ToolLog, dir string) *Toolbox {
        t.Helper()
 
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        t.Cleanup(func() {
index 3cb15b280687b891043f7f255a5346f935647f82..4f29cd4454ecec8a48d1b9878d29b7566b14a1b0 100644 (file)
@@ -387,7 +387,7 @@ func newTestTurnLoopWith(t *testing.T, server *ModelFake, summarizer Summarizer)
 
        agent.config.Workdir = t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), t.TempDir(), clock.Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), clock.Now, summarizer, discardLog())
        require.NoError(t, err)
 
        test := &TurnTestLoop{