Sync parity docs and strengthen replay gate coverage
Some checks failed
Release Gate / gate (push) Has been cancelled

This commit is contained in:
2026-04-03 19:34:14 +09:00
parent 5de5c74040
commit 3b03b18f83
6 changed files with 122 additions and 11 deletions

View File

@@ -486,6 +486,57 @@ public class AppStateServiceTests
state.RecentTasks[2].Kind.Should().Be("tool");
}
[Fact]
[Trait("Suite", "ReplayStability")]
public void RestoreRecentTasks_PermissionDeniedLeavesNoActivePermissionAfterResume()
{
var state = new AppStateService();
var now = DateTime.Now;
state.RestoreRecentTasks(
[
new ChatExecutionEvent { Timestamp = now.AddMinutes(-3), RunId = "run-11", Type = "PermissionRequest", ToolName = "file_write", Summary = "권한 확인", Iteration = 1 },
new ChatExecutionEvent { Timestamp = now.AddMinutes(-2), RunId = "run-11", Type = "PermissionDenied", ToolName = "file_write", Summary = "권한 거부", Success = false, Iteration = 2 },
new ChatExecutionEvent { Timestamp = now.AddMinutes(-1), RunId = "run-11", Type = "Thinking", Summary = "다음 단계 재계획", Iteration = 3 },
]);
state.ActiveTasks.Should().NotContain(t => t.Kind == "permission");
state.ActiveTasks.Should().Contain(t => t.Kind == "agent" && t.Status == "running");
state.RecentTasks.Should().Contain(t => t.Kind == "permission" && t.Status == "failed");
}
[Fact]
[Trait("Suite", "ReplayStability")]
public void ApplyAgentEvent_ReplaysHookTimelineInReverseChronologicalOrder()
{
var state = new AppStateService();
var now = DateTime.Now;
state.ApplyAgentEvent(new AgentEvent
{
RunId = "run-hook-replay",
Type = AgentEventType.HookResult,
ToolName = "__permission_request__",
Summary = "[Hook:a] first",
Timestamp = now.AddSeconds(-2),
Success = true,
});
state.ApplyAgentEvent(new AgentEvent
{
RunId = "run-hook-replay",
Type = AgentEventType.HookResult,
ToolName = "__permission_denied__",
Summary = "[Hook:b] second",
Timestamp = now,
Success = false,
});
var hooks = state.GetRecentHookEvents(2);
hooks.Should().HaveCount(2);
hooks[0].ToolName.Should().Be("__permission_denied__");
hooks[1].ToolName.Should().Be("__permission_request__");
}
[Fact]
public void ApplyAgentEvent_TracksPermissionLifecycleInTaskStore()
{

View File

@@ -257,6 +257,25 @@ public class TaskRunServiceTests
service.RecentTasks.Should().Contain(t => t.Kind == "agent" && t.Status == "completed");
}
[Fact]
[Trait("Suite", "ReplayStability")]
public void RestoreRecentFromExecutionEvents_CompleteClearsParallelToolCallsForSameRun()
{
var service = new TaskRunService();
var now = DateTime.Now;
service.RestoreRecentFromExecutionEvents(
[
new Models.ChatExecutionEvent { Timestamp = now.AddSeconds(-4), RunId = "run-parallel", Type = "ToolCall", ToolName = "file_read", Summary = "read", Iteration = 1 },
new Models.ChatExecutionEvent { Timestamp = now.AddSeconds(-3), RunId = "run-parallel", Type = "ToolCall", ToolName = "grep", Summary = "grep", Iteration = 1 },
new Models.ChatExecutionEvent { Timestamp = now.AddSeconds(-2), RunId = "run-parallel", Type = "ToolResult", ToolName = "file_read", Summary = "done", Success = true, Iteration = 2 },
new Models.ChatExecutionEvent { Timestamp = now.AddSeconds(-1), RunId = "run-parallel", Type = "Complete", Summary = "complete", Success = true, Iteration = 3 },
]);
service.ActiveTasks.Should().BeEmpty();
service.RecentTasks.Should().Contain(t => t.Kind == "agent" && t.Status == "completed");
}
[Fact]
public void RestoreRecentFromExecutionEvents_RunLevelErrorClearsDanglingRunScopedActiveTasks()
{