feat(ui): surface initial semantic reconstruction
This commit is contained in:
@@ -866,6 +866,10 @@ export default function ReasoningWorkspace({
|
|||||||
const [focusedPresentationItemId, setFocusedPresentationItemId] = useState(null);
|
const [focusedPresentationItemId, setFocusedPresentationItemId] = useState(null);
|
||||||
const [doneForNowIds, setDoneForNowIds] = useState([]);
|
const [doneForNowIds, setDoneForNowIds] = useState([]);
|
||||||
|
|
||||||
|
// ── RTO.29D — post-Analyse initial reflection surface ─────────
|
||||||
|
const [initialReflectionActive, setInitialReflectionActive] = useState(false);
|
||||||
|
const [postAnalyseStatus, setPostAnalyseStatus] = useState(null);
|
||||||
|
|
||||||
const { saveSession, loadSession } = useSessionPersistence();
|
const { saveSession, loadSession } = useSessionPersistence();
|
||||||
|
|
||||||
// Persist workspace state on every successful update (Phase 3)
|
// Persist workspace state on every successful update (Phase 3)
|
||||||
@@ -1110,6 +1114,28 @@ export default function ReasoningWorkspace({
|
|||||||
const hasCurrentSummaryCondition =
|
const hasCurrentSummaryCondition =
|
||||||
Boolean(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary) || !hasSelectedQuestion;
|
Boolean(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary) || !hasSelectedQuestion;
|
||||||
|
|
||||||
|
// ── RTO.29D — manage initial reflection lifecycle ────────────
|
||||||
|
useEffect(() => {
|
||||||
|
const entering = status === "success" && hasGraph && propUnderstanding && !hasSelectedQuestion;
|
||||||
|
if (entering) {
|
||||||
|
setPostAnalyseStatus("success");
|
||||||
|
setInitialReflectionActive(true);
|
||||||
|
} else if (!entering && initialReflectionActive) {
|
||||||
|
// Leave initial reflection when something changes (user interacts, status shifts)
|
||||||
|
setInitialReflectionActive(false);
|
||||||
|
setPostAnalyseStatus(null);
|
||||||
|
}
|
||||||
|
}, [status, result, hasGraph]);
|
||||||
|
|
||||||
|
// Deactivate initial reflection when user does anything meaningful
|
||||||
|
useEffect(() => {
|
||||||
|
if (!initialReflectionActive) return;
|
||||||
|
if (selectedPresentationItemId || focusedPresentationItemId || investigationHistory.length > 0 || formulationStep !== "idle" || processingStep !== "idle") {
|
||||||
|
setInitialReflectionActive(false);
|
||||||
|
setPostAnalyseStatus(null);
|
||||||
|
}
|
||||||
|
}, [selectedPresentationItemId, focusedPresentationItemId, investigationHistory, formulationStep, processingStep]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6" data-testid="reasoning-workspace">
|
<div className="space-y-6" data-testid="reasoning-workspace">
|
||||||
{/* ── Loading overlays ─────────────────────────────── */}
|
{/* ── Loading overlays ─────────────────────────────── */}
|
||||||
@@ -1150,6 +1176,46 @@ export default function ReasoningWorkspace({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{/* ── RTO.29D — initial post-Analyse reflection ──────── */}
|
||||||
|
{postAnalyseStatus === "success" && (
|
||||||
|
<div className="space-y-6" data-testid="initial-reflection-surface">
|
||||||
|
{/* What I understood */}
|
||||||
|
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-6 pt-5 pb-6">
|
||||||
|
<h2 className="mb-3 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
|
What I understood
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm leading-relaxed text-gray-700">{propUnderstanding}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Initial proposed findings */}
|
||||||
|
<div className="space-y-3" data-testid="initial-proposed-findings">
|
||||||
|
<h2 className="text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
|
Initial proposed findings
|
||||||
|
</h2>
|
||||||
|
{(() => {
|
||||||
|
const resolvedIds = new Set(graph?.resolvedNodeIds || []);
|
||||||
|
return (
|
||||||
|
graph?.nodes?.map((node) =>
|
||||||
|
node.kind === "unknown" && node.status !== "resolved" && !resolvedIds.has(node.id) ? (
|
||||||
|
<button
|
||||||
|
key={node.id}
|
||||||
|
onClick={() => startFocused(node.id)}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
className="w-full text-left rounded-lg border border-gray-200 bg-white px-5 py-4 transition hover:border-gray-300 hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
<span className="block text-sm leading-relaxed text-gray-900">{node.label}</span>
|
||||||
|
{node.description && node.description !== node.label && (
|
||||||
|
<p className="mt-1.5 text-xs leading-snug text-gray-500">{node.description}</p>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : null,
|
||||||
|
) || []
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ── Workspace grid: persistent whenever a graph exists ─── */}
|
{/* ── Workspace grid: persistent whenever a graph exists ─── */}
|
||||||
{(hasGraph || experimentalBranches) && (
|
{(hasGraph || experimentalBranches) && (
|
||||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
@@ -1219,7 +1285,9 @@ export default function ReasoningWorkspace({
|
|||||||
{/* Supporting context within conversation lane */}
|
{/* Supporting context within conversation lane */}
|
||||||
{hasCurrentSummaryCondition && (
|
{hasCurrentSummaryCondition && (
|
||||||
<>
|
<>
|
||||||
<CurrentUnderstandingCard currentSummary={graph?.currentSummary || result?.updatedSituationGraph?.currentSummary} plainLanguage={propUnderstanding || null} />
|
{postAnalyseStatus !== "success" && (
|
||||||
|
<CurrentUnderstandingCard currentSummary={graph?.currentSummary || result?.updatedSituationGraph?.currentSummary} plainLanguage={propUnderstanding || null} />
|
||||||
|
)}
|
||||||
{/* ── Experiment 12: progress panel A / B / C toggle (temporary experimental UI) — de-emphasised by branch-as-context experiment RTO.25D */}
|
{/* ── Experiment 12: progress panel A / B / C toggle (temporary experimental UI) — de-emphasised by branch-as-context experiment RTO.25D */}
|
||||||
{hasGraph && (
|
{hasGraph && (
|
||||||
<div className="hidden space-y-2">
|
<div className="hidden space-y-2">
|
||||||
@@ -1291,7 +1359,7 @@ export default function ReasoningWorkspace({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{graph && <OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />}
|
{graph && postAnalyseStatus !== "success" && <OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />}
|
||||||
{/* RTO.25B — temporarily hidden to reduce competing navigation while branch-experiment is active */}
|
{/* RTO.25B — temporarily hidden to reduce competing navigation while branch-experiment is active */}
|
||||||
<div className="hidden">
|
<div className="hidden">
|
||||||
<InvestigationMap turnCount={investigationHistory.length} />
|
<InvestigationMap turnCount={investigationHistory.length} />
|
||||||
|
|||||||
@@ -852,23 +852,25 @@ export default function ScenarioForm() {
|
|||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Branch switcher (1/4 sidebar) */}
|
{/* Branch switcher (1/4 sidebar) — suppressed during initial reflection */}
|
||||||
<div className="lg:col-span-1">
|
{!((status === "success" && result?.situationGraph && !result?.selectedQuestion)) && (
|
||||||
<ExperimentalBranchSwitcher
|
<div className="lg:col-span-1">
|
||||||
branches={BRANCHES}
|
<ExperimentalBranchSwitcher
|
||||||
activeBranchId={activeBranchId}
|
branches={BRANCHES}
|
||||||
branchNewResults={branchNewResults}
|
activeBranchId={activeBranchId}
|
||||||
branchPauseState={doneForNowBranchIds}
|
branchNewResults={branchNewResults}
|
||||||
onBranchSelect={(id) => {
|
branchPauseState={doneForNowBranchIds}
|
||||||
if (id === activeBranchId) return;
|
onBranchSelect={(id) => {
|
||||||
// Reopen: if the selected branch is paused, clear its pause state
|
if (id === activeBranchId) return;
|
||||||
if (doneForNowBranchIds.includes(id)) {
|
// Reopen: if the selected branch is paused, clear its pause state
|
||||||
setDoneForNowBranchIds(prev => prev.filter(x => x !== id));
|
if (doneForNowBranchIds.includes(id)) {
|
||||||
}
|
setDoneForNowBranchIds(prev => prev.filter(x => x !== id));
|
||||||
setActiveBranchId(id);
|
}
|
||||||
}}
|
setActiveBranchId(id);
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user