fix: unify reasoning mode across submissions
This commit is contained in:
@@ -177,3 +177,78 @@ History should remain collapsed unless the user chooses to inspect previous reas
|
||||
### Original situation
|
||||
|
||||
Once an investigation has started, the original scenario becomes reference material rather than the primary focus.
|
||||
|
||||
## Interaction Modes
|
||||
|
||||
The Confidence Engine operates in two distinct modes.
|
||||
|
||||
### Workspace Mode
|
||||
|
||||
The user is reading, thinking, and providing information.
|
||||
|
||||
The interface should:
|
||||
|
||||
- present the current investigation
|
||||
- allow the user to answer
|
||||
- show the current understanding
|
||||
- provide investigation history
|
||||
|
||||
The workspace is interactive.
|
||||
|
||||
---
|
||||
|
||||
### Reasoning Mode
|
||||
|
||||
The engine is analysing the available evidence.
|
||||
|
||||
The interface should:
|
||||
|
||||
- clearly indicate that reasoning is in progress
|
||||
- temporarily suspend the workspace
|
||||
- reassure the user that their input has been accepted
|
||||
- avoid displaying partial or changing reasoning
|
||||
|
||||
The workspace is paused until reasoning completes.
|
||||
|
||||
---
|
||||
|
||||
### Transition
|
||||
|
||||
Every submission follows the same lifecycle:
|
||||
|
||||
User submits information
|
||||
↓
|
||||
Workspace pauses
|
||||
↓
|
||||
Reasoning mode
|
||||
↓
|
||||
Updated workspace appears
|
||||
|
||||
The interaction should be identical whether the submission is:
|
||||
|
||||
- the initial situation
|
||||
- an investigation answer
|
||||
- a future uploaded document
|
||||
- any other evidence
|
||||
|
||||
Users should never wonder whether their input has been accepted or whether the engine is still reasoning.
|
||||
|
||||
---
|
||||
|
||||
## Consistent Submission Lifecycle
|
||||
|
||||
Every submission follows the same lifecycle regardless of context.
|
||||
|
||||
1. User submits → workspace pauses immediately, stale content disappears
|
||||
2. Reasoning mode appears with loading overlay (spinner, rotating status messages, elapsed timer)
|
||||
3. Updated workspace returns or error state appears
|
||||
|
||||
The loading card must appear before any async request begins and remain visible until the result arrives.
|
||||
|
||||
During reasoning mode:
|
||||
- The workspace is fully hidden (not just disabled)
|
||||
- No stale question, response form, or active indicators are visible
|
||||
- The Investigation Summary Panel shows "Reasoning" rather than "Investigation in progress"
|
||||
- Recovery states render on top of the loading overlay when applicable
|
||||
|
||||
This applies equally to initial analysis and all update submissions.
|
||||
|
||||
@@ -29,7 +29,7 @@ function humaniseDuration(seconds) {
|
||||
|
||||
/* ── Component ────────────────────────────────────────────── */
|
||||
|
||||
function InvestigationSummaryPanel({ graph, selectedQuestion, result }) {
|
||||
function InvestigationSummaryPanel({ graph, selectedQuestion, result, updateStatus }) {
|
||||
// ── Current status ────────────────────────────────────────────
|
||||
// TODO: reasoning should emit an explicit status field such as
|
||||
// "investigating", "evidence_limit_reached", "resolution_achieved".
|
||||
@@ -38,7 +38,9 @@ function InvestigationSummaryPanel({ graph, selectedQuestion, result }) {
|
||||
const hasGraph = Boolean(graph);
|
||||
|
||||
let currentStatus;
|
||||
if (!hasGraph) {
|
||||
if (updateStatus === "loading") {
|
||||
currentStatus = { label: "Reasoning", level: "investigating" };
|
||||
} else if (!hasGraph) {
|
||||
currentStatus = { label: "Not started", level: "idle" };
|
||||
} else if (isInvestigating) {
|
||||
currentStatus = { label: "Investigation in progress", level: "investigating" };
|
||||
|
||||
@@ -673,7 +673,7 @@ export default function ReasoningWorkspace({
|
||||
) : (
|
||||
<>
|
||||
{/* ── Investigation summary card ─────────────── */}
|
||||
<InvestigationSummaryPanel graph={graph} selectedQuestion={selectedQ} result={result} />
|
||||
<InvestigationSummaryPanel graph={graph} selectedQuestion={selectedQ} result={result} updateStatus={updateStatus} />
|
||||
|
||||
{/* ── Active investigation: question + form (top priority) ─ */}
|
||||
{canAnswer && (
|
||||
|
||||
@@ -443,7 +443,7 @@ export default function ScenarioForm() {
|
||||
)}
|
||||
|
||||
{/* ── Main result workspace ─────────────────────── */}
|
||||
{(status === "success" || status === "error" || updateStatus === "success") && (
|
||||
{(status === "success" || status === "error") && updateStatus !== "loading" && (
|
||||
<ReasoningWorkspace
|
||||
scenario={scenario}
|
||||
status={status}
|
||||
@@ -475,6 +475,16 @@ export default function ScenarioForm() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Update loading (reasoning mode) ─────────── */}
|
||||
{updateStatus === "loading" && status === "success" && (
|
||||
<LoadingOverlay
|
||||
isLoading={true}
|
||||
elapsed={updateElapsed}
|
||||
currentMessage={updateMsg}
|
||||
variant="update"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Continue later banner when session was restored ── */}
|
||||
{status === "success" && result?.updatedAt && (
|
||||
<ContinueLaterBanner onRestart={() => { clearSession(); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); }} />
|
||||
|
||||
@@ -305,3 +305,44 @@ test("loading overlay appears on submit and disappears after response", async ({
|
||||
// Normal journey test: wait directly for the stable resulting state.
|
||||
await waitForWorkspaceReady(page);
|
||||
});
|
||||
|
||||
/* ═══════ Test: update loading hides workspace and shows overlay ═ */
|
||||
|
||||
test("update submission hides workspace, shows reasoning overlay, then restores workspace", async ({ page }) => {
|
||||
// Use normal (700ms) delay so the loading overlay is observable during updates
|
||||
await page.goto("/");
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.__MOCK_ENABLED = true;
|
||||
window.__MOCK_DELAY = "normal";
|
||||
window.__MOCK_SCENARIO = "complete";
|
||||
});
|
||||
|
||||
// Start investigation
|
||||
const textarea = page.locator("textarea[placeholder*=Describe]");
|
||||
await textarea.fill(INVESTIGATION_SCENARIOS[0].centralStatement);
|
||||
await page.getByRole("button", { name: "Analyse" }).click();
|
||||
|
||||
await waitForWorkspaceReady(page);
|
||||
|
||||
// Workspace visible after initial analysis
|
||||
await expect(page.locator('[data-testid="reasoning-workspace"]')).toBeVisible();
|
||||
|
||||
// Submit an answer to trigger update loading
|
||||
const answerTextarea = page.locator('textarea[placeholder*=Answer]');
|
||||
if (await answerTextarea.isVisible({ timeout: 3000 })) {
|
||||
await answerTextarea.fill("The figures are comparable.");
|
||||
await page.getByRole("button", { name: "Update situation" }).click();
|
||||
|
||||
// Workspace should disappear during update loading (reasoning mode)
|
||||
await expect(page.locator('[data-testid="reasoning-workspace"]')).not.toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// Loading overlay should be visible (with role="status" for accessibility)
|
||||
const overlay = page.locator('[data-testid="loading-overlay"][role="status"]');
|
||||
await expect(overlay).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// Status text should indicate reasoning mode (update-specific copy)
|
||||
const statusText = overlay.getByRole("status");
|
||||
await expect(statusText).toHaveText(/Working through your situation/);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user