From 6598636206826c4ba52dcc126a28dc88ccda37eb Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Thu, 21 May 2026 09:13:06 -0700 Subject: [PATCH 1/2] fix: address code review and security audit findings - chronicle.go: prevent goroutine blocking by respecting ctx.Done in PTY reader - dbwatch.go: recover panics in onChange callback to keep watcher alive - plans.go: tighten session-state dir permissions from 0755 to 0700 - store.go: log searchRefs DB errors instead of silently swallowing them - update.go: clean up partial file on copyFile write failure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/data/chronicle.go | 6 +++++- internal/data/dbwatch.go | 10 +++++++++- internal/data/plans.go | 2 +- internal/data/store.go | 2 ++ internal/update/update.go | 1 + 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/data/chronicle.go b/internal/data/chronicle.go index 23c5cff..b11b881 100644 --- a/internal/data/chronicle.go +++ b/internal/data/chronicle.go @@ -99,7 +99,11 @@ func ChronicleReindex(ctx context.Context, onLine func(line string)) error { for { n, rErr := ptty.Read(buf) if n > 0 { - outCh <- string(buf[:n]) + select { + case outCh <- string(buf[:n]): + case <-ctx.Done(): + return + } } if rErr != nil { return diff --git a/internal/data/dbwatch.go b/internal/data/dbwatch.go index 572ddc7..6fd4a65 100644 --- a/internal/data/dbwatch.go +++ b/internal/data/dbwatch.go @@ -111,7 +111,15 @@ func (w *DBWatcher) loop() { continue } if w.Poll() && w.onChange != nil { - w.onChange() + func() { + defer func() { + if r := recover(); r != nil { + // Swallow panic so the watcher loop survives. + _ = r + } + }() + w.onChange() + }() } } } diff --git a/internal/data/plans.go b/internal/data/plans.go index 66b3f64..625322f 100644 --- a/internal/data/plans.go +++ b/internal/data/plans.go @@ -147,7 +147,7 @@ func WriteContinuationPlan(sessionID string, remaining []string, summary string) // Ensure the session directory exists (it may not if the session-state // was created by the store but never had a plan written). dir := filepath.Dir(path) - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o700); err != nil { return fmt.Errorf("creating session dir: %w", err) } diff --git a/internal/data/store.go b/internal/data/store.go index 4a52faa..1939909 100644 --- a/internal/data/store.go +++ b/internal/data/store.go @@ -784,6 +784,7 @@ func (s *Store) searchRefs(ctx context.Context, query string, limit int) []Searc rows, err := s.db.QueryContext(ctx, q, args...) if err != nil { + slog.Debug("searchRefs query failed", "error", err) return nil } defer rows.Close() //nolint:errcheck // rows read-only @@ -792,6 +793,7 @@ func (s *Store) searchRefs(ctx context.Context, query string, limit int) []Searc for rows.Next() { var r SearchResult if err := rows.Scan(&r.Content, &r.SessionID, &r.SourceType, &r.SourceID); err != nil { + slog.Debug("searchRefs scan failed", "error", err) continue } r.Rank = -100 // boost ref matches to top diff --git a/internal/update/update.go b/internal/update/update.go index 86fb106..f41ebec 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -619,6 +619,7 @@ func copyFile(src, dst string) error { if _, err := io.Copy(out, in); err != nil { _ = out.Close() + _ = os.Remove(dst) return err } From 1dc495ae9721266a990cec8775a9f597e3ba7e2b Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Thu, 21 May 2026 09:22:40 -0700 Subject: [PATCH 2/2] docs: fix stale --rebuild-index flag name in README The CLI flag was renamed to --reindex but the README still referenced the old --rebuild-index name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92f8c1f..6491491 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Dispatch reads your local Copilot CLI session store and presents every past sess - **Refresh** (`r`) — reload the session store without restarting - **Demo mode** — `dispatch --demo` with synthetic data for experimentation - **Self-update** — `dispatch update` checks GitHub Releases and upgrades in-place; background update check notifies on new versions -- **Maintenance** — `--rebuild-index` (manual rebuild of the session index via Copilot CLI PTY — repair action only), `--clear-cache` (reset config) +- **Maintenance** — `--reindex` (manual rebuild of the session index via Copilot CLI PTY — repair action only), `--clear-cache` (reset config) - **Cross-platform** — Windows (amd64/arm64), macOS (amd64/arm64), Linux (amd64/arm64) ### Feature Highlights