fix: clarify terminal investigation states
This commit is contained in:
@@ -25,11 +25,35 @@ function isTechnicalSummary(summary) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Current understanding card ────────────────────────────────
|
||||||
|
|
||||||
|
// Evidence-limit text that must not appear inside Current understanding
|
||||||
|
// when the terminal outcome already communicates that state.
|
||||||
|
const EVIDENCE_LIMIT_PHRASES = [
|
||||||
|
"The available evidence has reached its current limit",
|
||||||
|
"evidence has reached its current limit",
|
||||||
|
"evidence limit reached",
|
||||||
|
"has reached its current limit",
|
||||||
|
];
|
||||||
|
|
||||||
function resolveCurrentSummary(currentSummary) {
|
function resolveCurrentSummary(currentSummary) {
|
||||||
if (isTechnicalSummary(currentSummary)) {
|
if (!currentSummary || typeof currentSummary !== "string") return null;
|
||||||
return null;
|
const trimmed = currentSummary.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
|
||||||
|
// Filter out technical graph summaries
|
||||||
|
for (const p of TECHNICAL_PATTERNS) {
|
||||||
|
if (p.test(trimmed)) return null;
|
||||||
}
|
}
|
||||||
return currentSummary || null;
|
|
||||||
|
// Don't show evidence-limit text in Current understanding when
|
||||||
|
// the terminal outcome card already communicates that state.
|
||||||
|
const lower = trimmed.toLowerCase();
|
||||||
|
for (const phrase of EVIDENCE_LIMIT_PHRASES) {
|
||||||
|
if (lower.includes(phrase)) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Status message pools for loading feedback ────────────────
|
// ── Status message pools for loading feedback ────────────────
|
||||||
@@ -139,24 +163,17 @@ function CompletionCard() {
|
|||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-green-300 bg-green-50 px-5 py-6 text-center">
|
<div className="rounded-lg border border-green-300 bg-green-50 px-5 py-6 text-center">
|
||||||
<h2 className="mb-1 text-sm font-bold uppercase tracking-wide text-green-700">Investigation complete</h2>
|
<h2 className="mb-1 text-sm font-bold uppercase tracking-wide text-green-700">Investigation complete</h2>
|
||||||
<p className="text-base text-gray-800">You have provided enough information. The situation has been fully investigated.</p>
|
<p className="text-base text-gray-800">The current investigation has reached a justified conclusion.</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Evidence exhaustion card (terminal state) ─────────────────
|
// ── Evidence-limit card (terminal state: no next question) ───────
|
||||||
function EvidenceExhaustedCard({ noQuestionReason }) {
|
function EvidenceLimitCard() {
|
||||||
let message = "There is not yet enough evidence to guide the next step.";
|
|
||||||
if (noQuestionReason) {
|
|
||||||
const reason = String(noQuestionReason);
|
|
||||||
if (reason.toLowerCase().includes("insufficient")) {
|
|
||||||
message = reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-gray-200 bg-gray-50 px-5 py-6 text-center">
|
<div className="rounded-lg border border-gray-200 bg-gray-50 px-5 py-6 text-center">
|
||||||
<h2 className="mb-1 text-sm font-bold uppercase tracking-wide text-gray-500">Further investigation needed</h2>
|
<h2 className="mb-1 text-sm font-bold uppercase tracking-wide text-gray-500">Current evidence limit reached</h2>
|
||||||
<p className="text-base text-gray-700">{message}</p>
|
<p className="text-base text-gray-700">Further progress requires additional evidence.</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -432,8 +449,6 @@ export default function ReasoningWorkspace({
|
|||||||
const graph = result?.situationGraph ?? null;
|
const graph = result?.situationGraph ?? null;
|
||||||
const diagnostics = result?.diagnostics ?? null;
|
const diagnostics = result?.diagnostics ?? null;
|
||||||
const newlySurfacedNodeIds = result?.newlySurfacedNodeIds || [];
|
const newlySurfacedNodeIds = result?.newlySurfacedNodeIds || [];
|
||||||
const noQuestionReason = diagnostics?.noQuestionReason ?? null;
|
|
||||||
|
|
||||||
const genuineCompletion = hasGenuineCompletion(graph);
|
const genuineCompletion = hasGenuineCompletion(graph);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -457,68 +472,66 @@ export default function ReasoningWorkspace({
|
|||||||
{/* ── No graph produced after initial analysis ───────── */}
|
{/* ── No graph produced after initial analysis ───────── */}
|
||||||
{(status === "success" || status === "error") && !graph ? (
|
{(status === "success" || status === "error") && !graph ? (
|
||||||
<div className="rounded-lg border border-yellow-300 bg-yellow-50 px-4 py-3 text-sm text-yellow-800">
|
<div className="rounded-lg border border-yellow-300 bg-yellow-50 px-4 py-3 text-sm text-yellow-800">
|
||||||
{noQuestionReason
|
{diagnostics?.noQuestionReason
|
||||||
? "Validation failed — no structured graph output was produced."
|
? "Validation failed — no structured graph output was produced."
|
||||||
: "The analysis completed but did not produce a structured result."}
|
: "The analysis completed but did not produce a structured result."}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* ── 1. Current investigation (or terminal state) ─ */}
|
{/* ── 1. Terminal state: outcome card (no active question) ─ */}
|
||||||
{canAnswer && <CurrentInvestigationCard selectedQuestion={selectedQ} graph={graph} />}
|
{status === "success" && !canAnswer && graph && !isUpdating && genuineCompletion && <CompletionCard />}
|
||||||
|
{status === "success" && !canAnswer && graph && !isUpdating && !genuineCompletion && <EvidenceLimitCard />}
|
||||||
|
|
||||||
{/* Terminal states occupy the hero position when no question */}
|
{/* ── 2. Current understanding (when meaningful) ─ */}
|
||||||
{status === "success" && !canAnswer && graph && genuineCompletion && (
|
{(graph?.currentSummary || (status === "success" && !canAnswer)) && <CurrentUnderstandingCard currentSummary={graph.currentSummary} />}
|
||||||
<CompletionCard />
|
|
||||||
)}
|
|
||||||
{status === "success" && !canAnswer && graph && !genuineCompletion && updateStatus !== "success" && (
|
|
||||||
<EvidenceExhaustedCard noQuestionReason={noQuestionReason} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── 2. Post-update acknowledgement ─────────────── */}
|
{/* ── 3. Original situation (always-visible reference) ─ */}
|
||||||
{updateStatus === "success" && canAnswer && <UpdateAcknowledgement updateResult={result} />}
|
|
||||||
|
|
||||||
{/* ── 3. Response form ───────────────────────────── */}
|
|
||||||
{canAnswer && (
|
|
||||||
<form onSubmit={handleUpdateCaptureAndSubmit} className="space-y-4 rounded-lg border border-gray-200 bg-white p-5">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="rw-answer" className="mb-2 block text-sm font-medium text-gray-700">
|
|
||||||
Your response
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="rw-answer"
|
|
||||||
value={answer}
|
|
||||||
onChange={(e) => setAnswer(e.target.value)}
|
|
||||||
rows={4}
|
|
||||||
disabled={updateStatus === "loading"}
|
|
||||||
className="w-full rounded-lg border border-gray-300 px-4 py-3 text-sm focus:border-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-400 disabled:cursor-not-allowed disabled:opacity-60"
|
|
||||||
placeholder="Enter the answer to the selected question..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="text-xs text-gray-400">
|
|
||||||
{updateStatus === "loading" ? "Updating..." : "One update turn only in this prototype."}
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={updateStatus === "loading" || !answer.trim()}
|
|
||||||
className="rounded-lg bg-blue-700 px-5 py-2 text-sm font-medium text-white transition hover:bg-blue-600 disabled:cursor-not-allowed disabled:opacity-40"
|
|
||||||
>
|
|
||||||
{updateStatus === "loading" ? "Updating..." : "Update situation"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── 4. Current understanding ───────────────────── */}
|
|
||||||
{canAnswer && graph && <CurrentUnderstandingCard currentSummary={graph.currentSummary} />}
|
|
||||||
|
|
||||||
{/* ── 5. Original situation (always-visible reference) ─ */}
|
|
||||||
<OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />
|
<OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />
|
||||||
|
|
||||||
{/* ── 6. Investigation history ───────────────────── */}
|
{/* ── 4. Investigation history ───────────────────── */}
|
||||||
<InvestigationHistory turns={investigationHistory} />
|
{investigationHistory.length > 0 && <InvestigationHistory turns={investigationHistory} />}
|
||||||
|
|
||||||
{/* ── 7. Developer details (collapsed by default) ── */}
|
{/* ── Active investigation: question + form + acknowledgement ─ */}
|
||||||
|
{canAnswer && (
|
||||||
|
<>
|
||||||
|
<CurrentInvestigationCard selectedQuestion={selectedQ} graph={graph} />
|
||||||
|
|
||||||
|
{/* Post-update acknowledgement */}
|
||||||
|
{updateStatus === "success" && <UpdateAcknowledgement updateResult={result} />}
|
||||||
|
|
||||||
|
{/* Response form */}
|
||||||
|
<form onSubmit={handleUpdateCaptureAndSubmit} className="space-y-4 rounded-lg border border-gray-200 bg-white p-5">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="rw-answer" className="mb-2 block text-sm font-medium text-gray-700">
|
||||||
|
Your response
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="rw-answer"
|
||||||
|
value={answer}
|
||||||
|
onChange={(e) => setAnswer(e.target.value)}
|
||||||
|
rows={4}
|
||||||
|
disabled={updateStatus === "loading"}
|
||||||
|
className="w-full rounded-lg border border-gray-300 px-4 py-3 text-sm focus:border-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-400 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
|
placeholder="Enter the answer to the selected question..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<p className="text-xs text-gray-400">
|
||||||
|
{updateStatus === "loading" ? "Updating..." : "One update turn only in this prototype."}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={updateStatus === "loading" || !answer.trim()}
|
||||||
|
className="rounded-lg bg-blue-700 px-5 py-2 text-sm font-medium text-white transition hover:bg-blue-600 disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
|
>
|
||||||
|
{updateStatus === "loading" ? "Updating..." : "Update situation"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ── 5. Developer details (collapsed by default) ── */}
|
||||||
{(status === "success" || status === "error") && graph && (
|
{(status === "success" || status === "error") && graph && (
|
||||||
<DeveloperDetails
|
<DeveloperDetails
|
||||||
graph={graph}
|
graph={graph}
|
||||||
@@ -528,10 +541,6 @@ export default function ReasoningWorkspace({
|
|||||||
updateResult={updateStatus === "success" ? result : null}
|
updateResult={updateStatus === "success" ? result : null}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Terminal state with understanding (when no active question) ─ */}
|
|
||||||
{status === "success" && !canAnswer && graph && genuineCompletion && <CurrentUnderstandingCard currentSummary={graph.currentSummary} />}
|
|
||||||
{status === "success" && !canAnswer && graph && !genuineCompletion && updateStatus !== "success" && <CurrentUnderstandingCard currentSummary={graph.currentSummary} />}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useState, useRef, useMemo } from "react";
|
import { useState, useRef, useMemo } from "react";
|
||||||
import DiagnosticsView from "@/components/diagnostics-view";
|
import DiagnosticsView from "@/components/diagnostics-view";
|
||||||
import GraphUpdateView from "@/components/graph-update-view";
|
|
||||||
import SituationGraphView from "@/components/situation-graph-view";
|
|
||||||
import ReasoningWorkspace, { LoadingOverlay } from "@/components/reasoning-workspace";
|
import ReasoningWorkspace, { LoadingOverlay } from "@/components/reasoning-workspace";
|
||||||
import { mockFetch } from "@/lib/mocks/confidence-engine/mock-client";
|
import { mockFetch } from "@/lib/mocks/confidence-engine/mock-client";
|
||||||
|
|
||||||
@@ -103,14 +101,6 @@ export function ScenarioResultPanels({ status, result }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(status === "success" || hasGraph || hasQuestion) && (
|
|
||||||
<SituationGraphView
|
|
||||||
situationGraph={result.situationGraph}
|
|
||||||
selectedQuestion={result.selectedQuestion}
|
|
||||||
newlySurfacedNodeIds={result.newlySurfacedNodeIds}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hasDiagnostics && <DiagnosticsView result={result} />}
|
{hasDiagnostics && <DiagnosticsView result={result} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user