fix(goal): scope queue snapshot preservation

This commit is contained in:
mateaix 2026-09-15 09:49:59 +08:00
parent 6dc5fa201e
commit ab2589550e
4 changed files with 11 additions and 7 deletions

View File

@ -1630,7 +1630,6 @@ public class ChatController {
queuedConversation.getWorkspaceId(), null, baseUrl, preConsumedInput.requesterUserId())
.withOriginMessageId(queuedOriginMessageId)
.withSelectedGoalId(preConsumedInput.selectedGoalId());
queuedOrigin = captureWebGoal(queuedOrigin, agentId);
Disposable disposable = agentService.chatStructuredStream(agentId, queuedMessage, conversationId, preConsumedInput.createdBy(), null, queuedOrigin)
.doOnNext(delta -> {
if (emitterDone.get()) return;

View File

@ -38,7 +38,7 @@ public class GoalApprovalRunService {
public ChatOrigin captureSelectedGoal(ChatOrigin origin) {
if (origin == null || origin.cronOrigin() || origin.requesterUserId() == null
|| origin.conversationId() == null || origin.agentId() == null || origin.workspaceId() == null
|| origin.selectedGoalId() != null
|| (origin.selectedGoalId() != null && origin.selectedGoalId() > 0)
|| (origin.executionAttribution() != null
&& origin.executionAttribution().goalAttemptId() != null)) return origin;
var selected = jdbc.queryForList("""

View File

@ -82,7 +82,8 @@ class ChatControllerDurableQueueTest {
.thenReturn(reactor.core.publisher.Flux.never());
var runs = mock(vip.mate.goal.service.GoalApprovalRunService.class);
when(runs.queuedSelectionStillCurrent(any())).thenReturn(true);
when(runs.captureSelectedGoal(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(runs.captureSelectedGoal(any())).thenAnswer(invocation ->
((vip.mate.agent.context.ChatOrigin) invocation.getArgument(0)).withSelectedGoalId(7L));
ChatController controller = new ChatController(agents, conversations, mock(ApprovalWorkflowService.class), streams,
new ObjectMapper(), mock(ConversationCompletionPublisher.class), mock(MemoryOwnerResolver.class),
mock(ChatUploadLocationResolver.class), mock(OfficePreviewService.class), queue);
@ -198,7 +199,8 @@ class ChatControllerDurableQueueTest {
var runs = mock(vip.mate.goal.service.GoalApprovalRunService.class);
when(runs.hasManagedGoalHistory("conv", "2")).thenReturn(true);
when(runs.queuedSelectionStillCurrent(any())).thenReturn(true);
when(runs.captureSelectedGoal(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(runs.captureSelectedGoal(any())).thenAnswer(invocation ->
((vip.mate.agent.context.ChatOrigin) invocation.getArgument(0)).withSelectedGoalId(7L));
when(agents.chatStructuredStream(eq(2L), eq("next"), eq("conv"), eq("alice"), any(), any()))
.thenReturn(reactor.core.publisher.Flux.never());
ChatController controller = new ChatController(agents, conversations, mock(ApprovalWorkflowService.class),
@ -211,8 +213,11 @@ class ChatControllerDurableQueueTest {
new org.springframework.web.servlet.mvc.method.annotation.SseEmitter(),
new java.util.concurrent.atomic.AtomicBoolean(false), "alice", "http://localhost");
var origin = org.mockito.ArgumentCaptor.forClass(vip.mate.agent.context.ChatOrigin.class);
org.mockito.Mockito.verify(agents, org.mockito.Mockito.timeout(2000))
.chatStructuredStream(eq(2L), eq("next"), eq("conv"), eq("alice"), any(), any());
.chatStructuredStream(eq(2L), eq("next"), eq("conv"), eq("alice"), any(), origin.capture());
assertThat(origin.getValue().selectedGoalId()).isZero();
org.mockito.Mockito.verify(runs, org.mockito.Mockito.never()).captureSelectedGoal(any());
org.mockito.Mockito.verify(queue).consume(eq(92L), any(), any());
org.mockito.Mockito.verify(queue).consume(eq(93L), any(), any());
}

View File

@ -152,8 +152,8 @@ class GoalJsonHttpRuntimeIntegrationTest {
if (!plan && entry.equals("sync") && accepted) {
var explicitlyUnselected = ChatOrigin.web(conversation, username, 1L, null, null, userId)
.withAgent(agentId).withSelectedGoalId(0L);
assertEquals(0L, approvalRuns.captureSelectedGoal(explicitlyUnselected).selectedGoalId(),
"An explicit queue snapshot must not be recaptured into a later Goal");
assertEquals(goal.getId(), approvalRuns.captureSelectedGoal(explicitlyUnselected).selectedGoalId(),
"An ordinary in-flight request may tighten an explicit zero before approval persistence");
assertFalse(approvalRuns.queuedSelectionStillCurrent(explicitlyUnselected),
"An unselected queue snapshot must become stale when a managed Goal appears");
}