The windows-tests CI job flakes with:
TempDir RemoveAll cleanup: unlinkat ...\data\tui_state.db (or session.db):
The process cannot access the file because it is being used by another process.
Windows refuses to delete a file while a handle is open, and Go's testing
package retries for only 2s. Two things kept SQLite handles alive past
that window in the tuitest-driven e2e/tui tests:
- tui_state.db was closed only by the detached goroutine armed in
contextShutdownCmd, gated on ctx.Done() and never joined. tuitest's
cleanup quit the program but never released the model's resources, so
the close (WAL checkpoint + fsync on a slow runner) raced t.TempDir.
- sql.DB.Close only closes idle connections; one checked out by an
in-flight persistence write (token usage, title) is released after
Close has already returned, unobserved by the test.
Add sqliteutil.CloseDB, which closes and then waits (bounded) for every
connection to be released, and use it from the tuistate and session
stores. Export Shutdown on the TUI model and have tuitest.New call it
after the program stops, so every driver-based test closes tui_state.db
synchronously before rt.Close, store.Close and t.TempDir run.
Backup-and-reset recovery was firing on canceled contexts and
SQLITE_BUSY/LOCKED errors, silently discarding healthy session history.
New sqliteutil.IsTransientError gates the recovery path; regression
tests added in pkg/sqliteutil and pkg/session.
Assisted-By: Claude (Anthropic)
Thread context through sqlite/session/RAG/memory DB setup so constructors and migrations no longer mint fresh roots. Fake proxy cleanup and askpass connection handling now use context providers derived from their startup context. LocalRuntime stores a context provider for tool-change refresh callbacks.
The remaining suppressions are intentionally detached event hooks/MCP supervisor callbacks or compatibility fallbacks.
Annotate the remaining context.Background()/TODO() call sites that are
legitimate independent roots with //rubocop:disable Lint/ContextConnectivity.
These fall into a few categories that cannot (and should not) derive from
the program's root context:
- Bubble Tea event-loop handlers (pkg/tui): Update/Init and tea.Cmd
callbacks have no context parameter, and appModel deliberately does not
store the root ctx — threading it would make persistence writes
cancellable at shutdown.
- runtime.Runtime / interface implementations with fixed signatures
(remote_runtime, LocalRuntime Steer/FollowUp, accessors).
- Shutdown/flush paths that must outlive the cancelled parent ctx
(otel providers, HTTP server Shutdown, OAuth callback servers).
- sync.Once / memoized initializers and deliberately detached subprocesses
(gateway catalog, LSP process, sound, event hooks).
- Test/e2e helpers (pkg/fake, e2e/binary).
No behavior change.
Adds noctx to the enabled golangci-lint set and threads context through the HTTP, SQL, exec and net APIs it flags.
- Tests use t.Context() with the *Context / WithContext variants.
- Production code passes ctx where it is already in scope, uses context.Background() for genuine background work (DB schema migrations, sqlite Ping at startup, fire-and-forget audio playback) and adds //nolint:noctx for the few intentionally non-context-bound calls (tea.ExecProcess-driven interactive shells/editors and the explicit kill+WaitDelay flow in the shell tool).
While here, factor out the patterns that ended up duplicated:
- pkg/sqliteutil.CheckpointAndClose, used by the three RAG strategy databases.
- pkg/skills.httpGet, used by both cache.go and remote.go.
- pkg/sound.runDetached, used by all platform-specific play funcs.
- pkg/tools/mcp listenTCPRetry test helper, used in three places.
- pkg/tui handlers split into newInteractiveShellCmd + a single execCmd wrapper carrying the only //nolint:noctx (was four).
No behavior change.
Assisted-By: docker-agent
The messages JSON column on the sessions table was kept for backward
compatibility with older docker-agent versions. This is no longer needed.
- Remove syncMessagesColumn, syncMessagesColumnTx, syncMessagesColumnWith methods
- Remove loadMessagesFromLegacyColumn fallback
- Remove sync calls from AddMessage, UpdateMessage, AddSubSession, AddSummary
- Add migration 020 to DROP the messages column from the sessions table
- Remove 4 backward/forward compatibility tests that validated the column
Assisted-By: docker-agent
Add modernize and perfsprint to .golangci.yml and resolve all lint
issues across 97 files:
- Replace fmt.Errorf with errors.New where no format verbs are used
- Replace fmt.Sprintf(%d) with strconv.Itoa for int conversions
- Replace redundant fmt.Sprintf(%s) with direct string values
- Use slices.Contains instead of manual loops (SkillsConfig.HasLocal)
- Use omitzero instead of omitempty on struct fields (time.Time, SkillsConfig)
- Inline boolPtr helpers to new(bool) and remove unused functions
- Replace string concatenation in loops with strings.Builder
- Use fmt.Fprintf instead of WriteString(fmt.Sprintf(...))
Assisted-By: docker-agent
Fix three categories of error handling issues:
- Use errors.Is instead of == for error comparison (6 fixes)
io.EOF, sql.ErrNoRows, http.ErrServerClosed, context.Canceled
- Use errors.As instead of type assertion on errors (4 fixes)
*exec.ExitError, *environment.RequiredEnvError, cli.RuntimeError
- Use %w instead of %v in fmt.Errorf for proper wrapping (8 fixes)
api.go, eval.go, store.go, sqliteutil.go
Assisted-By: cagent
Adds a Ping() call to sqliteutil.OpenDB to ensure the database connection is valid and the file can be opened.
This ensures that file permission or missing directory errors are caught and diagnosed immediately.
Assisted-By: cagent
Creates a new pkg/sqliteutil package to handle SQLite connection opening with consistent pragmas.
Enables foreign key support (_pragma=foreign_keys(1)) globally, fixing ignored cascade deletes in RAG strategies.
Refactors pkg/memory, pkg/session, and pkg/rag to use the new utility.
Fixes a resource leak in NewMemoryDatabase where the connection wasn't closed on table creation failure.
Assisted-By: cagent