feat(ui): Mission Control Recent Sessions mit verschachtelten Handoffs
Neue Tabelle mission_control_sessions (Migration AddMissionControlSessions), befuellt vom InteractiveLaunchSpecService bei Task-, Ad-hoc- und Handoff-Kacheln. Flyout im Mission-Control-Header listet die letzten 14 Tage / 50 Sessions, Handoffs eingerueckt unter ihrer Ursprungs-Session, mit Fortsetzen (--resume im gespeicherten cwd) und Brief anzeigen.
This commit is contained in:
@@ -130,6 +130,8 @@ public abstract class StubWorkerClient : IWorkerClient
|
||||
=> Task.FromResult(new LaunchSpec(directory, "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<LaunchSpec> GetSessionHandoffLaunchSpecAsync(string paneId, string cwd, string briefPath, string? taskId, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(cwd, "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<LaunchSpec> GetResumeSessionLaunchSpecAsync(string sessionId, string paneId, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model = null, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<string> CreateMergeHelperTaskAsync(
|
||||
|
||||
@@ -42,6 +42,23 @@ public class MissionControlViewModelTests : IDisposable
|
||||
private MissionControlViewModel BuildVm(StubWorkerClient worker)
|
||||
=> new MissionControlViewModel(new TestDbFactory(NewContext), worker, new UsagePillViewModel(worker));
|
||||
|
||||
[Fact]
|
||||
public void RecentSessions_NestHandoffs_PutsIndirectHandoffsUnderTheirOrigin()
|
||||
{
|
||||
var t0 = new DateTime(2026, 9, 28, 8, 0, 0, DateTimeKind.Utc);
|
||||
MissionControlSessionEntity S(string id, int minutes, string? from = null)
|
||||
=> new() { SessionId = id, Cwd = "c", Title = id, StartedAt = t0.AddMinutes(minutes), PredecessorSessionId = from };
|
||||
// Newest first, as ListRecentAsync returns them. "orphan" names a predecessor outside the window.
|
||||
var newestFirst = new[] { S("c", 40, "b"), S("other", 30), S("b", 20, "a"), S("orphan", 15, "gone"), S("a", 10) };
|
||||
|
||||
var rows = MissionControlViewModel.NestHandoffs(newestFirst)
|
||||
.Select(r => (r.Session.SessionId, r.IsHandoff)).ToList();
|
||||
|
||||
Assert.Equal(
|
||||
new[] { ("a", false), ("b", true), ("c", true), ("other", false), ("orphan", false) },
|
||||
rows);
|
||||
}
|
||||
|
||||
// Gates the title/list-name DB lookup behind a manually-released TaskCompletionSource so two
|
||||
// overlapping Open*ConPtySessionAsync calls both run their synchronous dedupe-check prefix
|
||||
// to completion before either resumes past the DB await. This reproduces the open-path races
|
||||
|
||||
Reference in New Issue
Block a user