}
+ {/*
+ FNXC:PlanningMode 2026-07-15-00:00:
+ Awaiting-input questions are persisted database state, so transient idle SSE reconnects must not imply that the question is being regenerated. Reserve this hint for the active loading view, where live generation genuinely depends on the stream.
+ */}
+ {isReconnecting && view.type === "loading" &&
diff --git a/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx b/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx
index 3d0d34b924..bfa609371b 100644
--- a/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx
+++ b/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx
@@ -1935,6 +1935,79 @@ describe("PlanningModeModal", () => {
expect(screen.queryByRole("button", { name: "Refine Further" })).toBeNull();
});
+ it.each(["desktop", "mobile"] as const)("keeps persisted awaiting-input questions free of reconnecting hints on %s", async (viewportMode) => {
+ mockViewport(viewportMode);
+ let streamHandlers: any;
+ mockConnectPlanningStream.mockImplementationOnce((_sessionId: string, _projectId: string | undefined, handlers: any) => {
+ streamHandlers = handlers;
+ return { close: vi.fn(), isConnected: vi.fn().mockReturnValue(true) };
+ });
+ mockFetchAiSession.mockResolvedValueOnce({
+ id: `session-reconnect-question-${viewportMode}`,
+ type: "planning",
+ status: "awaiting_input",
+ title: "Persisted question",
+ inputPayload: JSON.stringify({ initialPlan: "Resume persisted question" }),
+ conversationHistory: "[]",
+ currentQuestion: JSON.stringify(mockQuestion),
+ result: null,
+ thinkingOutput: "",
+ error: null,
+ projectId: null,
+ createdAt: "2026-01-01T00:00:00.000Z",
+ updatedAt: "2026-01-01T00:00:00.000Z",
+ });
+
+ render(
+
,
+ );
+
+ expect(await screen.findByText(mockQuestion.question)).toBeInTheDocument();
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+
+ expect(screen.getByText(mockQuestion.question)).toBeInTheDocument();
+ expect(screen.queryByText("Reconnecting…")).toBeNull();
+ });
+
+ it("shows the reconnecting hint while active generation is loading", async () => {
+ let streamHandlers: any;
+ mockConnectPlanningStream.mockImplementationOnce((_sessionId: string, _projectId: string | undefined, handlers: any) => {
+ streamHandlers = handlers;
+ return { close: vi.fn(), isConnected: vi.fn().mockReturnValue(true) };
+ });
+
+ render(
+
,
+ );
+
+ fireEvent.change(screen.getByPlaceholderText(/e.g., Build a user authentication/), {
+ target: { value: "Generate a planning task" },
+ });
+ fireEvent.click(screen.getByText("Start Planning"));
+ await waitFor(() => expect(mockConnectPlanningStream).toHaveBeenCalledTimes(1));
+
+ act(() => {
+ streamHandlers.onConnectionStateChange?.("reconnecting");
+ });
+
+ expect(screen.getByText("Reconnecting…")).toBeInTheDocument();
+ });
+
it("shows summary view when resuming a complete persisted session", async () => {
const resumedSummary: PlanningSummary = {
title: "Resume-ready planning output",