fix(ui): warn and block save when a list's working dir isn't a git repo

Reuses RepoScanner.IsRepo (now public) to validate WorkingDir whenever it
changes in ListSettingsModalViewModel — Browse or typing. A missing folder
or a non-repo folder shows an inline warning and gates Save via the same
CanExecute pattern the load-failure gate already uses; an empty WorkingDir
is untouched. Previously this only surfaced as a Failed task deep in
WorktreeManager on first run.

Also localizes the two hardcoded EN strings in the same file (delete
confirm, planning-sessions block) into vm.listSettings.* keys.
This commit is contained in:
mika kuns
2026-09-10 13:32:52 +02:00
parent 752cf0926b
commit 3348736e9d
6 changed files with 69 additions and 7 deletions
@@ -161,7 +161,9 @@ public class ListSettingsModalViewModelTests
string? reported = null;
vm.ErrorReported += m => reported = m;
await vm.LoadAsync("l1", "List One", "/repo", "feat");
// Empty working dir: this test is about ticket-project load isolation, not the
// working-dir-is-a-repo gate added for the "not a git repo" Save check.
await vm.LoadAsync("l1", "List One", "", "feat");
Assert.Equal("make test", vm.VerifyCommand);
Assert.True(vm.PowerUseAllowed);
@@ -169,4 +171,45 @@ public class ListSettingsModalViewModelTests
Assert.NotNull(reported);
Assert.Contains("ticket system down", reported);
}
[Fact]
public async Task LoadAsync_WorkingDirNotAGitRepo_WarnsAndBlocksSave()
{
var plainDir = Directory.CreateTempSubdirectory().FullName;
var worker = new CountingWorker { Cfg = new ListConfigDto("opus", null, null) };
var vm = new ListSettingsModalViewModel(worker, NewFactory());
await vm.LoadAsync("l1", "List One", plainDir, "feat");
Assert.NotEmpty(vm.WorkingDirWarning);
Assert.False(vm.SaveCommand.CanExecute(null));
}
[Fact]
public async Task LoadAsync_WorkingDirIsAGitRepo_NoWarningAndSaveAllowed()
{
var repoDir = Directory.CreateTempSubdirectory().FullName;
Directory.CreateDirectory(Path.Combine(repoDir, ".git"));
var worker = new CountingWorker { Cfg = new ListConfigDto("opus", null, null) };
var vm = new ListSettingsModalViewModel(worker, NewFactory());
await vm.LoadAsync("l1", "List One", repoDir, "feat");
Assert.Empty(vm.WorkingDirWarning);
Assert.True(vm.SaveCommand.CanExecute(null));
}
[Fact]
public void WorkingDir_ClearedAfterNotARepoWarning_ClearsWarningAndAllowsSave()
{
var plainDir = Directory.CreateTempSubdirectory().FullName;
var worker = new CountingWorker { Cfg = new ListConfigDto("opus", null, null) };
var vm = new ListSettingsModalViewModel(worker, NewFactory()) { WorkingDir = plainDir };
Assert.NotEmpty(vm.WorkingDirWarning);
vm.WorkingDir = "";
Assert.Empty(vm.WorkingDirWarning);
Assert.True(vm.SaveCommand.CanExecute(null));
}
}