diff --git a/packages/dashboard/app/components/SubtaskBreakdownModal.tsx b/packages/dashboard/app/components/SubtaskBreakdownModal.tsx
index 24c6a0b016..b1148d6138 100644
--- a/packages/dashboard/app/components/SubtaskBreakdownModal.tsx
+++ b/packages/dashboard/app/components/SubtaskBreakdownModal.tsx
@@ -613,7 +613,11 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
{error &&
{error}
}
- {isReconnecting &&
{t("subtasks.reconnecting", "Reconnecting…")}
}
+ {/*
+ FNXC:SubtaskBreakdown 2026-07-15-00:00:
+ The persisted editing stored-review screen and in-flight creating state render from state, so transient SSE reconnects must not imply regeneration. Reserve the shared hint for active generating, which retains its intentional inline indicator.
+ */}
+ {isReconnecting && view.type === "generating" &&
{t("subtasks.reconnecting", "Reconnecting…")}
}
{view.type === "initial" && (
diff --git a/packages/dashboard/app/components/__tests__/MilestoneSliceInterviewModal.test.tsx b/packages/dashboard/app/components/__tests__/MilestoneSliceInterviewModal.test.tsx
index 17bfc74477..c9e82acdc9 100644
--- a/packages/dashboard/app/components/__tests__/MilestoneSliceInterviewModal.test.tsx
+++ b/packages/dashboard/app/components/__tests__/MilestoneSliceInterviewModal.test.tsx
@@ -393,6 +393,43 @@ describe("MilestoneSliceInterviewModal", () => {
expect(screen.getByText("Pick the size for this feature.")).toBeDefined();
});
});
+
+ it("shows reconnecting only during active generation, not on persisted questions", async () => {
+ mockStartMilestoneInterview.mockResolvedValue({ sessionId: "session-123" });
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByText("Start Interview"));
+ await waitFor(() => expect(streamHandlers).toBeDefined());
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+ expect(screen.getByText("Reconnecting…")).toBeInTheDocument();
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("connected");
+ streamHandlers.onQuestion?.(SAMPLE_QUESTION);
+ });
+ expect(await screen.findByText("What is the target scope?")).toBeInTheDocument();
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+
+ expect(screen.getByText("What is the target scope?")).toBeInTheDocument();
+ expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
+ });
});
describe("summary and apply", () => {
diff --git a/packages/dashboard/app/components/__tests__/MissionInterviewModal.test.tsx b/packages/dashboard/app/components/__tests__/MissionInterviewModal.test.tsx
index 5c802e53de..adcf8c2a35 100644
--- a/packages/dashboard/app/components/__tests__/MissionInterviewModal.test.tsx
+++ b/packages/dashboard/app/components/__tests__/MissionInterviewModal.test.tsx
@@ -259,7 +259,7 @@ describe("MissionInterviewModal", () => {
expect(mockForceAcquireSessionLock).not.toHaveBeenCalled();
});
- it("shows reconnecting indicator without clearing current question", async () => {
+ it("shows reconnecting only during active generation, not on persisted questions", async () => {
renderModal();
fireEvent.change(screen.getByLabelText("What do you want to build?"), {
@@ -273,26 +273,22 @@ describe("MissionInterviewModal", () => {
});
act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+ expect(screen.getByText("Reconnecting…")).toBeInTheDocument();
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("connected");
streamHandlers.onQuestion?.(SAMPLE_QUESTION);
});
-
expect(await screen.findByText("What is the target scope?")).toBeInTheDocument();
act(() => {
streamHandlers.onConnectionStateChange?.("reconnecting");
});
- expect(screen.getByText("Reconnecting…")).toBeInTheDocument();
- expect(screen.getByText("What is the target scope?")).toBeInTheDocument();
-
- act(() => {
- streamHandlers.onConnectionStateChange?.("connected");
- });
-
- await waitFor(() => {
- expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
- });
expect(screen.getByText("What is the target scope?")).toBeInTheDocument();
+ expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
});
it("preserves streaming thinking output while reconnecting", async () => {
@@ -390,7 +386,7 @@ describe("MissionInterviewModal", () => {
streamHandlers.onError?.("Stream error");
});
- expect(await screen.findByText("Reconnecting…")).toBeInTheDocument();
+ expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
expect(screen.getByText("What is the target scope?")).toBeInTheDocument();
expect(screen.queryByText("Stream error")).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Retry" })).not.toBeInTheDocument();
diff --git a/packages/dashboard/app/components/__tests__/SubtaskBreakdownModal.test.tsx b/packages/dashboard/app/components/__tests__/SubtaskBreakdownModal.test.tsx
index d4cfc574e9..18d8bb6897 100644
--- a/packages/dashboard/app/components/__tests__/SubtaskBreakdownModal.test.tsx
+++ b/packages/dashboard/app/components/__tests__/SubtaskBreakdownModal.test.tsx
@@ -302,28 +302,27 @@ describe("SubtaskBreakdownModal", () => {
expect(screen.getByDisplayValue("Do second")).toBeInTheDocument();
});
- it("shows reconnecting indicator without clearing subtask state", async () => {
+ it("shows reconnecting only during generation, not on persisted subtask review", async () => {
renderModal();
await waitFor(() => expect(streamHandlers).toBeDefined());
- streamHandlers.onSubtasks(SAMPLE_SUBTASKS);
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+ expect(screen.getAllByText("Reconnecting…")).toHaveLength(2);
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("connected");
+ streamHandlers.onSubtasks(SAMPLE_SUBTASKS);
+ });
expect(await screen.findByDisplayValue("First")).toBeInTheDocument();
act(() => {
streamHandlers.onConnectionStateChange?.("reconnecting");
});
- await waitFor(() => {
- expect(screen.getByText("Reconnecting…")).toBeInTheDocument();
- });
- expect(screen.getByDisplayValue("First")).toBeInTheDocument();
- act(() => {
- streamHandlers.onConnectionStateChange?.("connected");
- });
- await waitFor(() => {
- expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
- });
expect(screen.getByDisplayValue("First")).toBeInTheDocument();
+ expect(screen.queryByText("Reconnecting…")).not.toBeInTheDocument();
});
it("preserves thinking output while reconnecting in generating state", async () => {