Sort repository dropdown by most recently used#394
Conversation
Add LastUsedAt property to RepositoryInfo and sort the Repositories list by LastUsedAt descending (falling back to AddedAt for repos without usage history). TouchRepository() updates the timestamp when a session is created via CreateSessionWithWorktreeAsync. The dropdown in the new session form now shows the most recently used repository first, matching user expectations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR Review: Sort repository dropdown by most recently usedCI Status: 🟡 MODERATE — Unconditional
|
## Summary Two fixes in this PR: ### 1. Dead Event Stream Recovery (abort interrupted tools on resume) **Root cause:** When a session crashes mid-tool-execution (e.g., force-kill via `relaunch.ps1`), the SDK resumes the session but remains stuck waiting for `tool.execution_complete` results that will never arrive. All subsequent `SendAsync` calls are silently queued/ignored — zero events written to disk, zero callbacks fired. The session appears permanently stuck. **Evidence pattern in events.jsonl:** ``` tool.execution_start ← last real event before crash session.resume ← resume, zero new events after sends abort ← THIS unlocks the session user.message ← now everything works normally ``` **Fixes:** - **Abort on resume** (`CopilotService.Persistence.cs`): After `ResumeSessionAsync`, scan `events.jsonl` for unmatched `tool.execution_start` events. If found, send `AbortAsync` to clear the SDK's pending tool state before the user's message. - **HasInterruptedToolExecution helper** (`CopilotService.Utilities.cs`): Streams last 30 lines of `events.jsonl` via ring buffer. Scans backwards with correct reverse-order semantics (`pendingCompletions` counter). Handles both graceful shutdown and force-kill scenarios. - **INV-16 fix** (`CopilotService.Persistence.cs`): Moved `.On()` callback registration BEFORE `state.Session` assignment — closes a race window where events arrive with no handler registered. - **Watchdog Case D** (`CopilotService.Events.cs`): Safety net — if `events.jsonl` hasn't grown 30s after `SendAsync` and no tools are active, try `AbortAsync` to recover. Positioned after Case A/B with `!hasActiveTool` guard to avoid false positives. ### 2. Settings.razor Cleanup Removed orphaned **Copilot CLI** nav button and empty section shell from Settings. The underlying setting was moved to the **Developer** group in b94d0f4 but the nav button was left behind. ### Other Changes - `relaunch.ps1`: Fixed PowerShell 5.1 compatibility (bracket strings parsed as array indexers, `Join-Path` with 4 args) - `PolyPilot.csproj`: Updated MauiDevFlow packages 0.12.1 → 0.23.1 - `ChatDatabaseResilienceTests.cs`: Fixed Windows-specific flaky test (async file handle release) ### Invariants Validated - INV-1 ✅ INV-3 ✅ INV-4 ✅ INV-11 ✅ INV-12 ✅ INV-16 ✅ INV-17 ✅ ### Tests 2669/2669 passing --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ll sessions to hang (#390) ## Bug When the Copilot CLI persistent server's GitHub auth token expires, **all sessions** silently hang. The server stays TCP-alive (port check passes) but can't process requests. No `SessionErrorEvent` is sent — the watchdog eventually kills each session after ~135s. New sessions also get stuck. There is no recovery path. **User report:** "All my sessions can't be resumed because of auth issues and new sessions get stuck" ## Root Cause No mechanism to detect **server-wide** failures. `ConsecutiveStuckCount` tracks timeouts per-session, but the app never concludes the server itself is broken. ## Fix 1. **`IsAuthError()` helper** — detects auth-related exceptions (token expired, unauthorized, 401/403, credentials, etc.) 2. **`IsConnectionError()` extended** — now catches auth errors, enabling existing reconnect/restart recovery paths 3. **Service-level `_consecutiveWatchdogTimeouts` counter** — tracks timeouts across ALL sessions (not per-session) 4. **`TryRecoverPersistentServerAsync()`** — when counter reaches threshold (2), stops old server, starts fresh (forces re-authentication), recreates client 5. **Counter reset** on successful `CompleteResponse` (proves server health) 6. **`EnsureSessionConnectedAsync`** — catches auth errors on lazy resume and attempts recovery ## Testing - 31 new tests in `ServerRecoveryTests.cs` - All 2697 existing tests still pass (2 pre-existing failures unrelated) - Mac Catalyst build succeeds - UI scenario added for `persistent-server-auth-recovery` ## Files Changed - `CopilotService.Utilities.cs` — `IsAuthError()`, extended `IsConnectionError()` - `CopilotService.cs` — `_consecutiveWatchdogTimeouts`, `TryRecoverPersistentServerAsync()` - `CopilotService.Events.cs` — Increment counter in watchdog timeout, trigger recovery, reset on success - `CopilotService.Persistence.cs` — Auth error handling in `EnsureSessionConnectedAsync` - `ServerRecoveryTests.cs` — 31 new tests - `mode-switch-scenarios.json` — New scenario --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
…de lock - TouchRepository: move Save() inside if (repo != null) to avoid disk write when repo not found - CopilotService.CreateSessionWithWorktreeAsync: move TouchRepository inside the existing-worktree branch only; CreateWorktreeAsync/CreateWorktreeFromPrAsync already stamp LastUsedAt for new worktrees, so calling TouchRepository afterward was a redundant second stamp+save - Repositories getter: take list snapshot under lock, sort outside lock to minimize lock hold time Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-Review: Sort repository dropdown by most recently usedCI Status: Previous Findings — All Fixed ✅
All 5 models confirmed all 3 fixes. No new issue reached the 2+ model consensus threshold. 2708/2709 tests pass (1 pre-existing failure in Recommended Action: ✅ Approve |
Problem
The repository dropdown in the "New Session" form listed repos in insertion order, not by most recently used. Users with multiple repos had to scroll to find the one they just worked with.
Fix
LastUsedAtnullable property toRepositoryInfo(backward-compatible — missing field deserializes asnull)RepoManager.Repositoriesnow returns repos sorted byLastUsedAtdescending, falling back toAddedAtfor legacy entriesTouchRepository()method that stampsLastUsedAt = UtcNowand persistsTouchRepository()inCreateSessionWithWorktreeAsync(covers all session creation paths: new branch, PR checkout, and existing worktree)LastUsedAtinline inCreateWorktreeAsyncandCreateWorktreeFromPrAsyncTests
6 new tests in
RepoSortOrderTests.cs:All 2675 tests pass.