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 [doneForNowIds, setDoneForNowIds] = useState([]);
|
||||
|
||||
// ── RTO.29D — post-Analyse initial reflection surface ─────────
|
||||
const [initialReflectionActive, setInitialReflectionActive] = useState(false);
|
||||
const [postAnalyseStatus, setPostAnalyseStatus] = useState(null);
|
||||
|
||||
const { saveSession, loadSession } = useSessionPersistence();
|
||||
|
||||
// Persist workspace state on every successful update (Phase 3)
|
||||
@@ -1110,6 +1114,28 @@ export default function ReasoningWorkspace({
|
||||
const hasCurrentSummaryCondition =
|
||||
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 (
|
||||
<div className="space-y-6" data-testid="reasoning-workspace">
|
||||
{/* ── Loading overlays ─────────────────────────────── */}
|
||||
@@ -1150,6 +1176,46 @@ export default function ReasoningWorkspace({
|
||||
</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 ─── */}
|
||||
{(hasGraph || experimentalBranches) && (
|
||||
<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 */}
|
||||
{hasCurrentSummaryCondition && (
|
||||
<>
|
||||
{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 */}
|
||||
{hasGraph && (
|
||||
<div className="hidden space-y-2">
|
||||
@@ -1291,7 +1359,7 @@ export default function ReasoningWorkspace({
|
||||
</p>
|
||||
</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 */}
|
||||
<div className="hidden">
|
||||
<InvestigationMap turnCount={investigationHistory.length} />
|
||||
|
||||
@@ -852,7 +852,8 @@ export default function ScenarioForm() {
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Branch switcher (1/4 sidebar) */}
|
||||
{/* Branch switcher (1/4 sidebar) — suppressed during initial reflection */}
|
||||
{!((status === "success" && result?.situationGraph && !result?.selectedQuestion)) && (
|
||||
<div className="lg:col-span-1">
|
||||
<ExperimentalBranchSwitcher
|
||||
branches={BRANCHES}
|
||||
@@ -869,6 +870,7 @@ export default function ScenarioForm() {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user