Dashboard session management: Focus strip, enriched cards, and grid controls#413
Dashboard session management: Focus strip, enriched cards, and grid controls#413
Conversation
PR #413 Review: Dashboard session managementCI: 🔴 Consensus FindingsF1 🟡 MODERATE —
|
| Finding | Severity | Consensus |
|---|---|---|
| F1 Locale bug in scroll eval | 🟡 MODERATE | 5/5 |
| F2 Filter stuck/hidden | 🟡 MODERATE | 5/5 |
| F3 GetFocusSessions/SetFocusOverride lock missing | 🟡 MODERATE | 4/5 |
| F4 Shared menu state across focus+grid | 🟡 MODERATE | 4/5 |
Recommended action:
F1 and F2 are straightforward one-liners. F3 requires using the snapshot helpers already in the codebase. F4 needs a scoped key for the shared state. All four are fixable without structural changes.
F1: Fix locale-sensitive double in JS eval for scroll restoration - Use CultureInfo.InvariantCulture when interpolating _pendingScrollTop into eval string to prevent '1234,5' on German/French locales F2: Auto-reset status filter when filter bar hides - Reset _statusFilter to All when processingCount/attentionCount/stuckCount all drop to zero, preventing empty sidebar with no visible recovery path F3: Fix GetFocusSessions/SetFocusOverride missing _organizationLock - Use SnapshotSessionMetas()/SnapshotGroups() in GetFocusSessions() for thread-safe reads; take lock in SetFocusOverride before FirstOrDefault F4: Scope cardMenuSession/cardRenamingSession by focus strip prefix - Use 'focus:' + session.Name as key for focus strip cards to prevent opening the context menu on both focus and grid cards simultaneously - CommitCardRename strips the prefix before passing to RenameSession Minor: Replace hardcoded MessageCount=15 on focus strip cards with cardMessageCounts dict (same as main grid), so LoadMore works correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
22 new tests covering: Phase 4 healer (3 tests): - HealMultiAgentGroups_Phase4_ClearsWorkerRoleFromNonMultiAgentGroup - HealMultiAgentGroups_Phase4_ClearsOrchestratorRoleFromDeletedGroup - HealMultiAgentGroups_Phase2_PromotesGroupWithPersistedOrchestratorRole - HealMultiAgentGroups_Phase4_PreservesRolesInActualMultiAgentGroup GetFocusSessions logic (6 tests): - Returns processing sessions - Returns sessions with unread messages - FocusOverride.Included always shows - FocusOverride.Excluded never shows (even if processing) - Workers in real multi-agent groups excluded - Processing sessions sort before unread in Focus strip TolerantEnumConverter (9 tests): - Unknown string values fall back to default - Known string values (Worker, Orchestrator) deserialize correctly - Missing fields fall back to default - Case-insensitive matching - Serializes as string not integer - FocusOverride unknown value falls back to Auto - MultiAgentMode unknown value falls back to default UiState persistence (4 tests): - GridColumns default is 3 - CardMinHeight default is 250 - GridColumns and CardMinHeight round-trip serialization - Legacy JSON without new fields uses defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
F1: Fix locale-sensitive double in JS eval for scroll restoration - Use CultureInfo.InvariantCulture when interpolating _pendingScrollTop into eval string to prevent '1234,5' on German/French locales F2: Auto-reset status filter when filter bar hides - Reset _statusFilter to All when processingCount/attentionCount/stuckCount all drop to zero, preventing empty sidebar with no visible recovery path F3: Fix GetFocusSessions/SetFocusOverride missing _organizationLock - Use SnapshotSessionMetas()/SnapshotGroups() in GetFocusSessions() for thread-safe reads; take lock in SetFocusOverride before FirstOrDefault F4: Scope cardMenuSession/cardRenamingSession by focus strip prefix - Use 'focus:' + session.Name as key for focus strip cards to prevent opening the context menu on both focus and grid cards simultaneously - CommitCardRename strips the prefix before passing to RenameSession Minor: Replace hardcoded MessageCount=15 on focus strip cards with cardMessageCounts dict (same as main grid), so LoadMore works correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
22 new tests covering: Phase 4 healer (3 tests): - HealMultiAgentGroups_Phase4_ClearsWorkerRoleFromNonMultiAgentGroup - HealMultiAgentGroups_Phase4_ClearsOrchestratorRoleFromDeletedGroup - HealMultiAgentGroups_Phase2_PromotesGroupWithPersistedOrchestratorRole - HealMultiAgentGroups_Phase4_PreservesRolesInActualMultiAgentGroup GetFocusSessions logic (6 tests): - Returns processing sessions - Returns sessions with unread messages - FocusOverride.Included always shows - FocusOverride.Excluded never shows (even if processing) - Workers in real multi-agent groups excluded - Processing sessions sort before unread in Focus strip TolerantEnumConverter (9 tests): - Unknown string values fall back to default - Known string values (Worker, Orchestrator) deserialize correctly - Missing fields fall back to default - Case-insensitive matching - Serializes as string not integer - FocusOverride unknown value falls back to Auto - MultiAgentMode unknown value falls back to default UiState persistence (4 tests): - GridColumns default is 3 - CardMinHeight default is 250 - GridColumns and CardMinHeight round-trip serialization - Legacy JSON without new fields uses defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
232eb1f to
e63c50e
Compare
🔄 Re-Review Round 2 — PR #413Previous Findings Status
All four Round 1 findings are correctly addressed. The fixes are clean and follow established codebase patterns. New Issues (consensus: 2+ of 5 models)🟡 N1 (4/5 consensus) — Sort order comment vs. behavior mismatch In 🟡 N2 (2/5 consensus) — Strategy 2 fallback in 🟢 N3 (2/5 consensus) — Permanent Focus exclusion no longer reachable from UI
New Commits — No Additional Issues
Tests✅ 2852 passed, 0 failed (full suite, including all 54 WsBridgeIntegrationTests and 22 new DashboardFeatureTests) Verdict
N3 is a design note, not a blocker. |
…gement - Status filter chips (All/Processing/NeedsAttention/Stuck/Idle) in sidebar - Auto-hides when all sessions are idle (no interesting filters) - Shows counts per category - Cmd+K modal quick-switcher with fuzzy search across session metadata - Searches name, working directory, git branch, model - Keyboard navigation (arrows, Enter to select, Escape to close) - Shows status indicators and unread counts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Change @onkeydown to @onkeyup on quick-switcher input (avoids selection clear on re-render) - Add missing await on SelectQuickSwitcherSession call - Add StateHasChanged() to CloseQuickSwitcher for proper backdrop-click dismissal Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add reflection cycle progress pill on cards (goal, iteration progress bar, stop button) -- previously only visible in expanded view - Add stuck indicator banner when ConsecutiveStuckCount >= 1 - Add last-prompt preview for sessions with no loaded history - CSS: card-reflection-pill (active/paused), card-stuck-banner, card-last-prompt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rPrompt logic - Restore @if (PendingImages.Any()) guard in SessionCard.razor - Restore .attachment-strip { CSS selector in SessionCard.razor.css - Move LastUserPrompt preview inside card-messages scope so cardMsgs.Count == 0 logic works correctly (vs Session.History.Count == 0 which was always false) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rride
- Sessions with activity in last 48h auto-appear in sticky Focus strip at top of dashboard
- IsProcessing sessions always included in focus regardless of age
- FocusOverride enum on SessionMeta (Auto/Included/Excluded) for manual control
- SetFocusOverride() and GetFocusSessions() in CopilotService.Organization.cs
- Dismiss (✕) button on focus cards removes from strip (sets Excluded override)
- Context menu 'Add to Focus / Re-enable auto-focus' for manual inclusion
- Focus strip: horizontal flex, overflow-x:auto, sticky at top with z-index 100
- Fixed MoveSession declaration that was accidentally stripped during prior edit
- Fixed @{} in Razor else-block (moved to @if wrapper instead)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix DateTime.UtcNow to DateTime.Now for 48h cutoff (matches LastUpdatedAt storage) - Add position: sticky, top: 0, z-index: 100, max-height: 30vh to .focus-strip - Filter focus sessions out of normal group grid (no duplicates) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…roup filtering - Focus auto-detection: processing + unread only (not 48h window that caught all 45 sessions) - Cards in focus strip: flex: 0 0 380px, min-width: 380px, flex-shrink: 0 (no compression) - Revert group filtering: focus sessions appear in both strip AND their group (overlay, not filter) - Remove unused focusNames variable from focus strip block Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add +/- buttons in dashboard toolbar to set cards per row (1-6) - Grid uses repeat(N, 1fr) based on user choice (default 3) - GridColumns property persisted in UiState (ui-state.json) - Restored on dashboard load with bounds validation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mns everywhere - Remove Compact/Expanded button per user request (too many UI controls) - Fix Focus strip label from stale '48h' to 'Processing or unread' - Apply grid-template-columns inline style to both main and external grids - Remove dead .expanded-grid CSS rule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…in 2 cols - Change focus strip from flex (fixed 380px cards) to CSS grid with grid-template-columns controlled by _gridColumns - Raise minimum columns from 1 to 2 (1 col makes cards too wide) - Clamp persisted GridColumns < 2 back to default 3 - Both focus strip and group grids now respect the same column count - Increase focus strip max-height from 30vh to 40vh for better visibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove overflow-y:hidden from focus strip (was clipping card content) - Add min-height:250px to focus strip cards so they show messages - Expand focus detection: IsProcessing OR UnreadCount>0 OR active in last 10min This catches orchestrator sessions between dispatches and sessions awaiting user response that aren't technically 'processing' - Sort focus: processing first, then unread, then by recency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reduce recency window from 10min to 2min to avoid stale sessions - Exclude worker sessions (Role==Worker) from focus strip — they show under their orchestrator in the group grid below - Update hint text to 'Recently active' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Root cause: LastUpdatedAt defaulted to DateTime.Now on session creation, so every restored session appeared 'just updated' even if dormant for days. - Add LastUpdatedAt to ActiveSessionEntry (persisted in active-sessions.json) - Save LastUpdatedAt in both debounced and immediate save paths - Restore real LastUpdatedAt during session restore (overrides DateTime.Now default) - Change focus recency window from 2min to 24h as user requested - Sessions truly idle for >24h will no longer appear in Focus strip Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two bugs fixed: 1. Sessions without a SessionMeta entry were excluded from Focus (line 660 returned false). Now sessions pass through to the activity check even without meta — catches newly created sessions and sessions where reconciliation hasn't run yet. 2. Focus strip was position:sticky with z-index:100, making it overlay content on scroll. Removed sticky positioning — Focus is now a normal inline section at the top of the dashboard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sessions weren't appearing in Focus because LastUpdatedAt was only updated by SDK event handlers. Two missing triggers: 1. SendPromptAsync: now sets LastUpdatedAt = DateTime.Now alongside IsProcessing, so the session enters Focus immediately on send. 2. SetActiveSession: now updates LastUpdatedAt when the user switches to a session, so just reading a session marks it active for 24h. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Workers were unconditionally excluded from Focus (line 662). But when a user sends a message directly to a worker session, it should appear in Focus since the user is actively interacting with it. Changed: workers now show in Focus if they are processing or have recent activity (LastUpdatedAt within 24h). Idle workers without direct interaction remain hidden under their orchestrator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Root cause: SessionMeta.Role defaulted to MultiAgentRole.Worker, and the enum had no 'None' value. Every session created in any group was born as a Worker, even standalone sessions in regular groups. This caused the Focus strip to filter them out. Changes: 1. Added MultiAgentRole.None as the default (ordinal 0) 2. Changed SessionMeta.Role default from Worker to None 3. Added Phase 4 to HealMultiAgentGroups: clears stale Worker/ Orchestrator roles from sessions in non-multi-agent groups 4. GetFocusSessions now cross-checks group.IsMultiAgent before treating a Worker-labeled session as a real worker 5. Updated tests and fallback references Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Workers in actual multi-agent groups (group.IsMultiAgent=true) should never appear in Focus — they show under their orchestrator card in the group grid. Previous commit allowed them through if they had recent activity, which cluttered Focus with 'PR Review Squad-worker-1' etc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three fixes for quick-switcher keyboard navigation: 1. Changed from @onkeyup to @onkeydown so arrow keys respond immediately (not on release) and can be prevented from moving the text cursor. 2. Added @onkeydown:preventDefault with a flag that's set only for ArrowUp/ArrowDown/Enter/Escape — prevents cursor jumping while still allowing normal typing. 3. Added scroll-into-view for the selected item so long result lists keep the highlight visible. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Blazor @onkeydown:preventDefault attribute is evaluated at render time, not event time. This caused a race: arrow keys set the flag to true, but the browser had already decided not to prevent default (it was false at render). On the next render, preventDefault was true, potentially blocking normal typing. Fix: moved arrow/Enter/Escape handling to a JS keydown listener installed when the quick-switcher opens. JS calls preventDefault synchronously for navigation keys, then invokes C# via JSInterop for state updates. Normal character keys pass through to @oninput. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Removed entire Cmd+K quick-switcher feature (HTML, CSS, JS, C# code). Dashboard focus is now purely on the reorganized grid. 2. Fixed external sessions grid having a duplicate closing quote/angle bracket in the HTML tag: ">" was rendered as literal text — the tiny '>' cursor visible in the screenshot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Blazor DOM diffs from StateHasChanged/SafeRefreshAsync were causing the dashboard to jump back to the top on every update. Now SafeRefreshAsync saves the .dashboard scrollTop via JS before triggering StateHasChanged, and OnAfterRenderAsync restores it. Only applies in grid view (not expanded session mode). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… control - saveCardScrollPositions() JS fn saves scrollTop per session key before StateHasChanged - restoreDraftsAndFocus restores saved positions on re-render (no more scroll-to-top) - forceScroll only scrolls to bottom when explicitly needed (new message sent) - Add _cardMinHeight field (150-600px, step 50, default 250) - Add CardMinHeight to UiState + SaveUiState, persisted to ui-state.json - Add height +/- buttons in toolbar (next to column controls) - Apply --card-min-height CSS variable to focus strip and grid cards Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename DefaultSessionMeta_RoleIsWorker -> DefaultSessionMeta_RoleIsNone - Assert MultiAgentRole.None (the actual default) instead of Worker - Update FocusOverride doc comments from '48h' to '24h' (matches GetFocusSessions) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…CSS variable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unknown enum string values now fall back to default(T) instead of throwing, preventing entire organization payload deserialization failures when desktop and mobile have mismatched enum definitions. Applied to: FocusOverride, SessionSortMode, MultiAgentMode, WorktreeStrategy, MultiAgentRole. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
F1: Fix locale-sensitive double in JS eval for scroll restoration - Use CultureInfo.InvariantCulture when interpolating _pendingScrollTop into eval string to prevent '1234,5' on German/French locales F2: Auto-reset status filter when filter bar hides - Reset _statusFilter to All when processingCount/attentionCount/stuckCount all drop to zero, preventing empty sidebar with no visible recovery path F3: Fix GetFocusSessions/SetFocusOverride missing _organizationLock - Use SnapshotSessionMetas()/SnapshotGroups() in GetFocusSessions() for thread-safe reads; take lock in SetFocusOverride before FirstOrDefault F4: Scope cardMenuSession/cardRenamingSession by focus strip prefix - Use 'focus:' + session.Name as key for focus strip cards to prevent opening the context menu on both focus and grid cards simultaneously - CommitCardRename strips the prefix before passing to RenameSession Minor: Replace hardcoded MessageCount=15 on focus strip cards with cardMessageCounts dict (same as main grid), so LoadMore works correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
22 new tests covering: Phase 4 healer (3 tests): - HealMultiAgentGroups_Phase4_ClearsWorkerRoleFromNonMultiAgentGroup - HealMultiAgentGroups_Phase4_ClearsOrchestratorRoleFromDeletedGroup - HealMultiAgentGroups_Phase2_PromotesGroupWithPersistedOrchestratorRole - HealMultiAgentGroups_Phase4_PreservesRolesInActualMultiAgentGroup GetFocusSessions logic (6 tests): - Returns processing sessions - Returns sessions with unread messages - FocusOverride.Included always shows - FocusOverride.Excluded never shows (even if processing) - Workers in real multi-agent groups excluded - Processing sessions sort before unread in Focus strip TolerantEnumConverter (9 tests): - Unknown string values fall back to default - Known string values (Worker, Orchestrator) deserialize correctly - Missing fields fall back to default - Case-insensitive matching - Serializes as string not integer - FocusOverride unknown value falls back to Auto - MultiAgentMode unknown value falls back to default UiState persistence (4 tests): - GridColumns default is 3 - CardMinHeight default is 250 - GridColumns and CardMinHeight round-trip serialization - Legacy JSON without new fields uses defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sort order (oldest-waiting-first for triage workflow): - Tier 0: Processing sessions (active work) — always at top - Tier 1: Unhandled sessions — oldest LastUpdatedAt first (longest waiting at top, most urgent) - Tier 2: Handled sessions — most recently handled at bottom Replace ✕ 'Dismiss' button with ✓ 'Handled' button: - Green circular button (matching the completion/success color) - Sets HandledAt on SessionMeta → moves session to bottom of Focus - HandledAt cleared automatically when CompleteResponse fires (new AI response = fresh activity, session moves back to top) - SessionMeta.HandledAt persists across restarts Rebase on origin/main (1 new commit from main merged cleanly). Fix RenderThrottleTests regression — HandledAt clearing moved to after OnStateChanged to stay within the source-order assertion window that CompleteResponse_OnSessionComplete_FiresBeforeOnStateChanged tests. Add tests: GetFocusSessions_HandledSessions_SortToBottom, GetFocusSessions_OldestWaitingFirst_WithinUnhandled, MarkFocusHandled_SetsHandledAtOnMeta, MarkFocusHandled_NonExistentSession_DoesNotThrow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PP- prefix healer fix (Phase 1 suffix-match):
- Orchestrators like 'PP- PR Review Squad-orchestrator' have a scoped
namespace prefix that their workers lack ('PR Review Squad-worker-N').
Old Phase 1 required exact prefix match → roles cleared by Phase 4 →
group permanently broken.
- Added IsWorkerForTeamPrefix() helper: tries exact prefix first, then
falls back to suffix-match (workerPrefix is a suffix of orchPrefix).
- Phase 3 worker collection uses the same helper for reconstruction.
- Added HealerPrefixMatchTests (5 tests covering exact, suffix, no
false-positive, no-duplicate-group, and full reconstruction cases).
WsBridge lock screen recovery:
- AcceptLoopAsync now catches HttpListenerException and sets _listener=null
instead of breaking — the restart loop at the top of the method calls
TryRestartListenerAsync() to revive the listener.
- TryRestartListenerAsync: brief 500ms delay, then tries wildcard and
localhost prefixes with exponential back-off (max 30s) on repeat failures.
- RunKeepalivePingAsync: detects when Task.Delay woke >1.5x late (machine
was suspended during lock screen) and triggers CheckConnectionHealthAsync.
- CheckConnectionHealthAsync: new public method — pings the headless server
on resume; if ping fails in Persistent mode, fires TryRecoverPersistentServerAsync.
- App.OnResume: calls CheckConnectionHealthAsync so the connection is checked
immediately when the user unlocks the Mac.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add namespace-prefix validation (must end with '- ') to prevent false suffix matches - Workers excluded from Focus strip regardless of group state - Add Phase1_SuffixMatch_NoFalsePositive test - Fix WsBridge comment indentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ing test
DashboardFeatureTests and HealerPrefixMatchTests both call
SetBaseDirForTesting() but were not in the BaseDir xUnit collection,
causing them to race with BaseDir-serialized tests and produce flaky
failures (HealMultiAgentGroups_DoesNothing_WhenGroupsAlreadyCorrect,
StateChangeCoalescerTests.SingleCall_FiresExactlyOnce).
- Add [Collection("BaseDir")] to DashboardFeatureTests and HealerPrefixMatchTests
- Restore TestSetup.TestBaseDir after each CreateServiceWithOrg() call
- Increase SingleCall_FiresExactlyOnce wait from 300ms → 600ms to survive
heavy CI load (timer fires at 150ms; 300ms was borderline under full suite)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5f11f37 to
16b91f5
Compare
🔄 Re-Review Round 3 — PR #413Previous Findings Status
New Commits Review (c5753d1 + 16b91f5)Worker exclusion simplification (
Test isolation ( New Issues (consensus: 2+ of 5 models required)None. No new consensus issues found in the two new commits. (Non-consensus note from 1 model: Tests✅ 2897 passed, 0 failed (full suite, including all WsBridgeIntegrationTests and DashboardFeatureTests) Verdict✅ Approve N1 (the sort comment) is the sole remaining nit from Round 2 — it's a misleading inline comment with no behavioral impact (the Focus strip renders correctly; in practice users rarely have multiple simultaneous processing sessions). Both N2 blocking issues from Round 2 are resolved, all Round 1 findings remain fixed, and the test suite is fully green. This is ready to merge. |
…ck consistency - GetFocusSessions: invert Processing-tier sort key via DateTime.MaxValue.Ticks - ticks so newest processing session appears first (matches comment 'most recent first') - MarkFocusHandled: move HandledAt write inside _organizationLock for consistency with SaveOrganizationCore snapshot pattern Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔄 Re-Review Round 3 — PR #413Previous Findings Status
New Fix (consensus 2+/5 models, committed in
|
🔄 Re-Review Round 3 — PR #413Previous Findings Status
New Commits Since Round 2
All three are sound. No new bugs introduced. Outstanding Issues (consensus: 2+ of 5 models)🟢 N1 (5/5 consensus) — Wrong inline comment in if (s.IsProcessing) return s.LastUpdatedAt; // Processing: most recent first ← WRONG
The behavior itself is likely intentional — showing the longest-running active job first is good triage UX, consistent with the Unhandled tier. The commit message for 🟢 N3 (3/5 consensus) — var listener = new HttpListener();
listener.Prefixes.Add(prefix);
listener.Start(); // throws → catch logs and continues
// listener never disposedIf the wildcard prefix ( Tests✅ 2896 passed, 1 failure ( All 54 HealerPrefixMatchTests and DashboardFeatureTests pass. Verdict
N3 (HttpListener dispose) is a minor style correctness note, not a blocker. All functional issues from prior rounds are resolved. The new PP- prefix healer, WsBridge lock-screen recovery, and test isolation fixes are all correct. |
🔄 Re-Review Round 4 — PR #413Previous Finding Status
All Findings Resolved
Tests✅ 2897 passed, 0 failed (full suite) Verdict: ✅ ApproveAll findings resolved across 4 rounds. No remaining issues. Ready to merge. |
Summary
Reworks the PolyPilot dashboard for managing 50+ sessions efficiently — the goal is to spend 95% of time in the dashboard view without needing to expand individual sessions.
Features
🎯 Focus Strip
FocusOverrideenum onSessionMeta(Auto/Included/Excluded), persisted inorganization.json📋 Enriched Session Cards
ConsecutiveStuckCount >= 1📐 Grid Layout Controls
ui-state.jsonacross restarts🔍 Status Filter Bar
📍 Scroll Position Preservation
.card-messagesscroll positions preserved per session key_needsScrollToBottom)Bug Fixes
MultiAgentRole.Noneadded as default to prevent mass mislabeling of regular sessions as WorkersHealMultiAgentGroupsclears stale Worker/Orchestrator roles from non-multi-agent groupsLastUpdatedAtpersistence — activity timestamps tracked across app restarts viaactive-sessions.jsonFiles Changed
Dashboard.razor/Dashboard.razor.css— Focus strip, grid controls, scroll preservationSessionCard.razor/SessionCard.razor.css— enriched card contentSessionSidebar.razor/SessionSidebar.razor.css— status filter barSessionOrganization.cs—FocusOverrideenum onSessionMetaCopilotService.Organization.cs—GetFocusSessions(),SetFocusOverride()CopilotService.cs—GridColumns,CardMinHeightonUiState;MultiAgentRole.NoneCopilotService.Persistence.cs—SaveUiStateextended for grid/height settingsindex.html—saveCardScrollPositions()JS helper