feat(ui): triage model picker (opus/fable) in the merge helper start modal
This commit is contained in:
@@ -497,6 +497,8 @@
|
||||
"windowTitle": "Claude machen lassen",
|
||||
"scopeList": "Liste: {0}",
|
||||
"scopeReview": "Alle Listen — Tasks im Review",
|
||||
"modelLabel": "Modell",
|
||||
"modelTip": "Modell für die Triage-Session (Dedupe & Verfeinern). Spätere Merge-Phasen laufen immer auf Opus.",
|
||||
"confirm": "Claude machen lassen",
|
||||
"cancel": "Abbrechen",
|
||||
"selectAll": "Alle auswählen",
|
||||
|
||||
@@ -497,6 +497,8 @@
|
||||
"windowTitle": "Let Claude handle it",
|
||||
"scopeList": "List: {0}",
|
||||
"scopeReview": "All lists — tasks waiting for review",
|
||||
"modelLabel": "Model",
|
||||
"modelTip": "Model for the triage session (dedupe & refine). Later merge phases always run on opus.",
|
||||
"confirm": "Let Claude handle it",
|
||||
"cancel": "Cancel",
|
||||
"selectAll": "Select all",
|
||||
|
||||
@@ -143,8 +143,9 @@ public interface IWorkerClient : INotifyPropertyChanged
|
||||
/// <summary>Launch spec for an embedded ConPTY "merge helper" session that drives the given
|
||||
/// tasks to a merged/Done state. listId scopes the session (and cwd) to that list.
|
||||
/// handlerTaskId (from CreateMergeHelperTaskAsync) is rendered into the brief so the session
|
||||
/// can call handoff_list_handler on its own handler task.</summary>
|
||||
Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct = default);
|
||||
/// can call handoff_list_handler on its own handler task. model overrides the triage
|
||||
/// session's model (null = default).</summary>
|
||||
Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model = null, CancellationToken ct = default);
|
||||
/// <summary>Creates the ClaudeDo task that owns a list-handler run (one per run, Idle/IsManual,
|
||||
/// never queued) so the ConPTY tile can be task-based instead of ad-hoc. Returns the new task id.</summary>
|
||||
Task<string> CreateMergeHelperTaskAsync(
|
||||
|
||||
@@ -627,8 +627,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
public async Task<LaunchSpec> GetAdHocLaunchSpecAsync(string directory, CancellationToken ct = default)
|
||||
=> await InvokeTimedAsync<LaunchSpec>("GetAdHocLaunchSpec", () => _hub.InvokeAsync<LaunchSpec>("GetAdHocLaunchSpec", directory, ct));
|
||||
|
||||
public async Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct = default)
|
||||
=> await InvokeTimedAsync<LaunchSpec>("GetMergeHelperLaunchSpec", () => _hub.InvokeAsync<LaunchSpec>("GetMergeHelperLaunchSpec", taskIds, listId, handlerTaskId, ct));
|
||||
public async Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model = null, CancellationToken ct = default)
|
||||
=> await InvokeTimedAsync<LaunchSpec>("GetMergeHelperLaunchSpec", () => _hub.InvokeAsync<LaunchSpec>("GetMergeHelperLaunchSpec", taskIds, listId, handlerTaskId, model, ct));
|
||||
|
||||
public async Task<string> CreateMergeHelperTaskAsync(
|
||||
IReadOnlyList<string> taskIds, string listId, string title, string descriptionHeader, CancellationToken ct = default)
|
||||
|
||||
@@ -17,8 +17,9 @@ namespace ClaudeDo.Ui.ViewModels.Islands;
|
||||
|
||||
public enum ListKind { Smart, Virtual, User }
|
||||
|
||||
/// <summary>Confirmed handler run: the scope list and the ordered selected task ids.</summary>
|
||||
public sealed record MergeHelperRequest(string ListId, IReadOnlyList<string> TaskIds);
|
||||
/// <summary>Confirmed handler run: the scope list, the ordered selected task ids, and the model
|
||||
/// picked for the triage session (null = default; review-mode runs skip triage entirely).</summary>
|
||||
public sealed record MergeHelperRequest(string ListId, IReadOnlyList<string> TaskIds, string? TriageModel = null);
|
||||
|
||||
public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
@@ -114,7 +115,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
|
||||
await vm.LoadAsync();
|
||||
var ids = await Dialogs.ShowMergeHelperSelectionAsync(vm);
|
||||
if (ids is { Count: > 0 })
|
||||
LetClaudeHandleRequested?.Invoke(new MergeHelperRequest(rawId, ids));
|
||||
LetClaudeHandleRequested?.Invoke(new MergeHelperRequest(rawId, ids, vm.SelectedModel));
|
||||
}
|
||||
|
||||
/// <summary>Raised once per list after the review-scoped selection dialog is confirmed;
|
||||
|
||||
@@ -370,7 +370,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
||||
Lists.LetClaudeHandleRequested += req =>
|
||||
{
|
||||
OpenMissionControl();
|
||||
_ = MissionControl.OpenMergeHelperConPtySessionAsync(req.ListId, req.TaskIds);
|
||||
_ = MissionControl.OpenMergeHelperConPtySessionAsync(req.ListId, req.TaskIds, req.TriageModel);
|
||||
};
|
||||
Lists.MergeReviewsRequested += req =>
|
||||
{
|
||||
|
||||
@@ -331,10 +331,10 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
// creates one new ClaudeDo task per run to own the session (title/diff/result), deduped by
|
||||
// TaskId like OpenConPtySessionAsync. The handler still merges the tasks it handles itself
|
||||
// (no worktree of its own) — see TaskEntity.HandlerBaseCommit/HandlerHeadCommit.
|
||||
public System.Threading.Tasks.Task OpenMergeHelperConPtySessionAsync(string listId, IReadOnlyList<string> taskIds)
|
||||
public System.Threading.Tasks.Task OpenMergeHelperConPtySessionAsync(string listId, IReadOnlyList<string> taskIds, string? triageModel = null)
|
||||
=> OpenListHandlerConPtySessionAsync(listId, taskIds,
|
||||
"missionControl.mergeHelperTitle", "missionControl.mergeHelperTaskTitle",
|
||||
taskId => _worker.GetMergeHelperLaunchSpecAsync(taskIds, listId, taskId));
|
||||
taskId => _worker.GetMergeHelperLaunchSpecAsync(taskIds, listId, taskId, triageModel));
|
||||
|
||||
// Merge-only list-handler session over a list's WaitingForReview tasks (the review list's
|
||||
// "Let Claude merge the reviews"). Same host-task shape as the full run, but the session
|
||||
|
||||
@@ -67,6 +67,15 @@ public sealed partial class MergeHelperSelectionModalViewModel : ViewModelBase
|
||||
|
||||
public ObservableCollection<MergeHelperTaskRowViewModel> Tasks { get; } = new();
|
||||
|
||||
/// <summary>Model choices for the triage session. Only opus/fable are offered on purpose —
|
||||
/// triage is the dedupe/refine leverage point; cheaper models aren't worth a picker slot.</summary>
|
||||
public IReadOnlyList<string> ModelOptions { get; } = new[] { "opus", "fable" };
|
||||
|
||||
[ObservableProperty] private string _selectedModel = Data.Models.ModelRegistry.HandlerTriageAlias;
|
||||
|
||||
/// <summary>Hidden in review mode — those runs start directly at the merge phase, no triage.</summary>
|
||||
public bool ShowModelPicker => !_reviewMode;
|
||||
|
||||
[ObservableProperty] private string _scopeLabel = "";
|
||||
|
||||
public bool HasTasks => Tasks.Count > 0;
|
||||
@@ -100,6 +109,7 @@ public sealed partial class MergeHelperSelectionModalViewModel : ViewModelBase
|
||||
{
|
||||
_reviewMode = true;
|
||||
ScopeLabel = Loc.T("modals.mergeHelper.scopeReview");
|
||||
OnPropertyChanged(nameof(ShowModelPicker));
|
||||
}
|
||||
|
||||
/// <summary>Groups a confirmed review-mode selection by owning list, preserving row order —
|
||||
|
||||
@@ -29,11 +29,18 @@
|
||||
|
||||
<!-- Body: scope + select-all/none toolbar, headers, checkbox rows -->
|
||||
<DockPanel>
|
||||
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,Auto,Auto" Margin="20,12,20,6">
|
||||
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,Auto,Auto,Auto" Margin="20,12,20,6">
|
||||
<TextBlock Grid.Column="0" Classes="meta" Text="{Binding ScopeLabel}" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="1" Classes="btn" Content="{loc:Tr modals.mergeHelper.selectAll}"
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6" Margin="0,0,12,0"
|
||||
IsVisible="{Binding ShowModelPicker}"
|
||||
ToolTip.Tip="{loc:Tr modals.mergeHelper.modelTip}">
|
||||
<TextBlock Classes="meta" Text="{loc:Tr modals.mergeHelper.modelLabel}" VerticalAlignment="Center"/>
|
||||
<ComboBox MinWidth="90" ItemsSource="{Binding ModelOptions}"
|
||||
SelectedItem="{Binding SelectedModel, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="2" Classes="btn" Content="{loc:Tr modals.mergeHelper.selectAll}"
|
||||
Command="{Binding SelectAllCommand}" IsEnabled="{Binding HasTasks}"/>
|
||||
<Button Grid.Column="2" Classes="btn" Content="{loc:Tr modals.mergeHelper.selectNone}"
|
||||
<Button Grid.Column="3" Classes="btn" Content="{loc:Tr modals.mergeHelper.selectNone}"
|
||||
Command="{Binding SelectNoneCommand}" IsEnabled="{Binding HasTasks}" Margin="8,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -770,12 +770,13 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
// Builds the launch spec for an embedded ConPTY "merge helper" session that drives the given
|
||||
// tasks to a merged/Done state via the mcp__claudedo__* tools. listId scopes the brief label
|
||||
// and cwd to that list. handlerTaskId is the id returned by CreateMergeHelperTask -- it is
|
||||
// rendered into the brief so the session can call handoff_list_handler on itself.
|
||||
public Task<LaunchSpec> GetMergeHelperLaunchSpec(string[] taskIds, string listId, string handlerTaskId) => HubGuard(() =>
|
||||
// rendered into the brief so the session can call handoff_list_handler on itself. model
|
||||
// overrides the triage session's model (null = default opus).
|
||||
public Task<LaunchSpec> GetMergeHelperLaunchSpec(string[] taskIds, string listId, string handlerTaskId, string? model) => HubGuard(() =>
|
||||
{
|
||||
if (_interactiveLaunchSpec is null)
|
||||
throw new InvalidOperationException("Interactive launch spec service is not configured.");
|
||||
return _interactiveLaunchSpec.BuildForMergeHelperAsync(taskIds, listId, handlerTaskId, Context.ConnectionAborted);
|
||||
return _interactiveLaunchSpec.BuildForMergeHelperAsync(taskIds, listId, handlerTaskId, model, Context.ConnectionAborted);
|
||||
});
|
||||
|
||||
// Creates the ClaudeDo task that owns a list-handler run, before the ConPTY tile opens --
|
||||
|
||||
@@ -290,8 +290,10 @@ public sealed class InteractiveLaunchSpecService
|
||||
/// handlerTaskId (the id returned by CreateMergeHelperTaskAsync) is rendered into the brief so
|
||||
/// the session can call handoff_list_handler/submit_task_for_review on its own handler task.
|
||||
/// Throws KeyNotFoundException if the list doesn't exist; InvalidOperationException if
|
||||
/// taskIds is empty or the list has no existing working directory.</summary>
|
||||
public async Task<LaunchSpec> BuildForMergeHelperAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct)
|
||||
/// taskIds is empty or the list has no existing working directory.
|
||||
/// model overrides the triage session's model (start-modal dropdown); null/unknown falls
|
||||
/// back to <see cref="ModelRegistry.HandlerTriageAlias"/>. Handoff phases are unaffected.</summary>
|
||||
public async Task<LaunchSpec> BuildForMergeHelperAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model, CancellationToken ct)
|
||||
{
|
||||
if (taskIds.Count == 0)
|
||||
throw new InvalidOperationException("No tasks selected for the list handler.");
|
||||
@@ -344,12 +346,13 @@ public sealed class InteractiveLaunchSpecService
|
||||
// longer influences a handler session at all, it would otherwise apply the LIST's effort
|
||||
// preset to a completely different model.
|
||||
var settings = await new AppSettingsRepository(ctx).GetAsync(ct);
|
||||
var triageModel = ModelRegistry.TryNormalizeAlias(model) ?? ModelRegistry.HandlerTriageAlias;
|
||||
|
||||
var args = new List<string>
|
||||
{
|
||||
"--model", ModelRegistry.HandlerTriageAlias,
|
||||
"--effort", EffortFor(settings, ModelRegistry.HandlerTriageAlias),
|
||||
"--permission-mode", PermissionModeResolver.Resolve(ModelRegistry.HandlerTriageAlias, "auto"),
|
||||
"--model", triageModel,
|
||||
"--effort", EffortFor(settings, triageModel),
|
||||
"--permission-mode", PermissionModeResolver.Resolve(triageModel, "auto"),
|
||||
"--allowedTools", MergeHelperAllowedTools,
|
||||
"--add-dir", sessionDir, repoDir,
|
||||
"--append-system-prompt-file", systemPromptPath,
|
||||
|
||||
@@ -128,7 +128,7 @@ public abstract class StubWorkerClient : IWorkerClient
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<LaunchSpec> GetAdHocLaunchSpecAsync(string directory, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(directory, "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public virtual Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct = default)
|
||||
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(
|
||||
IReadOnlyList<string> taskIds, string listId, string title, string descriptionHeader, CancellationToken ct = default)
|
||||
|
||||
@@ -556,7 +556,7 @@ public class MissionControlViewModelTests : IDisposable
|
||||
|
||||
private sealed class ThrowingMergeHelperLaunchSpecWorker : StubWorkerClient
|
||||
{
|
||||
public override Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct = default)
|
||||
public override Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model = null, CancellationToken ct = default)
|
||||
=> throw new InvalidOperationException("spec failed");
|
||||
}
|
||||
|
||||
|
||||
@@ -721,7 +721,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
{
|
||||
var svc = BuildService(now: now);
|
||||
var spec = await svc.BuildForMergeHelperAsync(
|
||||
new[] { taskId }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
new[] { taskId }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
return await File.ReadAllTextAsync(Path.Combine(sessionDir, "system-prompt.md"));
|
||||
}
|
||||
@@ -851,7 +851,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
var listId = await SeedListAsync(workingDir: _tempDir);
|
||||
var svc = BuildService();
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => svc.BuildForMergeHelperAsync(Array.Empty<string>(), listId, Guid.NewGuid().ToString(), CancellationToken.None));
|
||||
() => svc.BuildForMergeHelperAsync(Array.Empty<string>(), listId, Guid.NewGuid().ToString(), null, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -863,7 +863,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
|
||||
var svc = BuildService();
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => svc.BuildForMergeHelperAsync(new[] { taskId }, listId, Guid.NewGuid().ToString(), CancellationToken.None));
|
||||
() => svc.BuildForMergeHelperAsync(new[] { taskId }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None));
|
||||
Assert.Contains("working directory", ex.Message);
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
|
||||
var svc = BuildService();
|
||||
await Assert.ThrowsAsync<KeyNotFoundException>(
|
||||
() => svc.BuildForMergeHelperAsync(new[] { taskId }, "no-such-list", Guid.NewGuid().ToString(), CancellationToken.None));
|
||||
() => svc.BuildForMergeHelperAsync(new[] { taskId }, "no-such-list", Guid.NewGuid().ToString(), null, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -892,7 +892,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
await SeedTaskAsync(t2, listId, TaskStatus.Idle, title: "Second task");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
Assert.Equal(repo, spec.Cwd);
|
||||
@@ -934,6 +934,27 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
Assert.Equal(InteractiveLaunchSpecService.McpToolTimeoutMs, spec.Env["MCP_TOOL_TIMEOUT"]);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("fable", "fable")]
|
||||
[InlineData("opus", "opus")]
|
||||
[InlineData("not-a-model", ModelRegistry.HandlerTriageAlias)] // unknown → default, never blocks the launch
|
||||
public async Task BuildForMergeHelperAsync_ModelOverride_DrivesModelAndEffortArgs(string requested, string expected)
|
||||
{
|
||||
var repo = Path.Combine(_tempDir, $"repoModel-{requested}");
|
||||
Directory.CreateDirectory(repo);
|
||||
var listId = await SeedListAsync(workingDir: repo, name: "Alpha");
|
||||
var t1 = Guid.NewGuid().ToString();
|
||||
await SeedTaskAsync(t1, listId, TaskStatus.Idle, title: "First task");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), requested, CancellationToken.None);
|
||||
TrackSessionDir(spec);
|
||||
|
||||
var args = spec.Args.ToList();
|
||||
Assert.Equal(expected, args[args.IndexOf("--model") + 1]);
|
||||
Assert.Equal(ModelPresets.For(ModelPresets.Defaults, expected).Effort, args[args.IndexOf("--effort") + 1]);
|
||||
}
|
||||
|
||||
// A directory argument that keeps its trailing separator escapes its own closing quote once the
|
||||
// ConPTY host flattens Args into a single Windows command line ("C:\repo\" -> \" is a literal
|
||||
// quote), so --add-dir's variadic list swallows every following argument -- including the
|
||||
@@ -949,7 +970,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
await SeedTaskAsync(t1, listId, TaskStatus.WaitingForReview, title: "First task");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var args = spec.Args.ToList();
|
||||
@@ -972,7 +993,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
await SeedTaskAsync(t2, listId, TaskStatus.Idle, title: "Second task");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var brief = File.ReadAllText(Path.Combine(sessionDir, "brief.md"));
|
||||
@@ -999,7 +1020,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
var handlerTaskId = Guid.NewGuid().ToString();
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, handlerTaskId, CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, handlerTaskId, null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var brief = File.ReadAllText(Path.Combine(sessionDir, "brief.md"));
|
||||
@@ -1018,7 +1039,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
description: "Do the thing carefully and report back.");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var brief = File.ReadAllText(Path.Combine(sessionDir, "brief.md"));
|
||||
@@ -1036,7 +1057,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
await SeedTaskAsync(t1, listId, TaskStatus.Idle, title: "No description task", description: null);
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var brief = File.ReadAllText(Path.Combine(sessionDir, "brief.md"));
|
||||
@@ -1067,7 +1088,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
await SeedTaskAsync(t2, listId, TaskStatus.WaitingForReview, title: "Second task", description: "plain description");
|
||||
|
||||
var svc = BuildService();
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), CancellationToken.None);
|
||||
var spec = await svc.BuildForMergeHelperAsync(new[] { t1, t2 }, listId, Guid.NewGuid().ToString(), null, CancellationToken.None);
|
||||
var sessionDir = TrackSessionDir(spec);
|
||||
|
||||
var brief = File.ReadAllText(Path.Combine(sessionDir, "brief.md"));
|
||||
@@ -1402,7 +1423,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
|
||||
var svc = BuildService();
|
||||
|
||||
var triageSpec = await svc.BuildForMergeHelperAsync(new[] { survivor }, listId, handlerTaskId, CancellationToken.None);
|
||||
var triageSpec = await svc.BuildForMergeHelperAsync(new[] { survivor }, listId, handlerTaskId, null, CancellationToken.None);
|
||||
var triageDir = TrackSessionDir(triageSpec);
|
||||
var executeSpec = await svc.BuildForMergeHelperHandoffAsync(handlerTaskId, new[] { survivor }, "merge", CancellationToken.None);
|
||||
var executeDir = TrackSessionDir(executeSpec);
|
||||
@@ -1453,7 +1474,7 @@ public sealed class InteractiveLaunchSpecServiceTests : IDisposable
|
||||
ClaudeSessionId: "sess-42", Token: "tok-2", WorktreePath: _worktreeDir);
|
||||
|
||||
var svc = BuildService();
|
||||
var mergeHelperSpec = await svc.BuildForMergeHelperAsync(new[] { survivor }, listId, handlerTaskId, CancellationToken.None);
|
||||
var mergeHelperSpec = await svc.BuildForMergeHelperAsync(new[] { survivor }, listId, handlerTaskId, null, CancellationToken.None);
|
||||
TrackSessionDir(mergeHelperSpec);
|
||||
var handoffSpec = await svc.BuildForMergeHelperHandoffAsync(handlerTaskId, new[] { survivor }, "wait", CancellationToken.None);
|
||||
TrackSessionDir(handoffSpec);
|
||||
|
||||
Reference in New Issue
Block a user