From 86bb3426ef7b747c21f73acfb08fccbf236003e3 Mon Sep 17 00:00:00 2001 From: robbond Date: Thu, 20 Aug 2026 09:00:01 +0100 Subject: [PATCH] test(ui): explore provisional branch pause and reopen --- components/experimental/branch-switcher.jsx | 18 +++++- components/reasoning-workspace.jsx | 57 ++++++++++++++--- components/scenario-form.jsx | 68 ++++++++++++++++++--- 3 files changed, 122 insertions(+), 21 deletions(-) diff --git a/components/experimental/branch-switcher.jsx b/components/experimental/branch-switcher.jsx index 2abd32e..7270ca9 100644 --- a/components/experimental/branch-switcher.jsx +++ b/components/experimental/branch-switcher.jsx @@ -46,7 +46,7 @@ function NewIndicator({ visible }) { /* ── Single branch row ─────────────────────────────────── */ -function BranchRow({ id, label, active, isNew, origin, onClick }) { +function BranchRow({ id, label, active, isNew, isPaused, origin, onClick }) { const isActive = Boolean(active); return ( @@ -80,8 +80,18 @@ function BranchRow({ id, label, active, isNew, origin, onClick }) { )} - {/* Passive new-result indicator (only for inactive branches) */} - {!isActive && } + {/* Passive indicators: pause + new */} + + {!isActive && isPaused && ( + + Paused + + )} + {!isActive && } + ); } @@ -92,6 +102,7 @@ export default function ExperimentalBranchSwitcher({ branches = [], activeBranchId, branchNewResults = {}, + branchPauseState = [], onBranchSelect, }) { if (!branches.length) return null; @@ -119,6 +130,7 @@ export default function ExperimentalBranchSwitcher({ label={branch.label} active={activeBranchId === branch.id} isNew={Boolean(branchNewResults[branch.id])} + isPaused={branchPauseState.includes(branch.id)} origin={branch.origin} onClick={() => onBranchSelect?.(branch.id)} /> diff --git a/components/reasoning-workspace.jsx b/components/reasoning-workspace.jsx index c7475ae..e829bc4 100644 --- a/components/reasoning-workspace.jsx +++ b/components/reasoning-workspace.jsx @@ -351,9 +351,14 @@ function BranchNotebookContent({ openQuestions, lateResults, inactiveBranchNewResults, + doneForNowBranchIds, + onDoneForNow, + branchId, }) { const hasOpenItems = openQuestions && openQuestions.length > 0; const hasContributions = contributions && contributions.length > 0; + const isPaused = doneForNowBranchIds?.includes(branchId) || false; + const isBranchActive = !isPaused; return (
@@ -407,9 +412,35 @@ function BranchNotebookContent({ )} - {/* Quiet state — only when there are no open items and no updates */} + {/* Done for now control — only when branch is not already paused */} + {isBranchActive && onDoneForNow && contributions.length > 0 && ( +
+ +
+ )} + + {/* Quiet state */} {!hasOpenItems && !lateResults?.length && ( - +
+ {isPaused ? ( + <> +

+ Nothing more to add here right now +

+

+ This branch is paused. Reasoning preserved — return when you have more information. +

+ + ) : ( + + )} +
)}
); @@ -805,6 +836,11 @@ export default function ReasoningWorkspace({ branchLocalContributions, branchLocalLateResults, inactiveBranchNewResults, + activeBranchIdForNotebook, + // ── RTO.27B — provisional pause state ── + doneForNowBranchIds, + onDoneForNow, + onReopenBranch, }) { const [investigationHistory, setInvestigationHistory] = useState([]); const turnCounter = useRef(0); @@ -1115,6 +1151,9 @@ export default function ReasoningWorkspace({ openQuestions={branchLocalQuestions || []} lateResults={branchLocalLateResults || []} inactiveBranchNewResults={inactiveBranchNewResults || {}} + doneForNowBranchIds={doneForNowBranchIds || []} + onDoneForNow={onDoneForNow} + branchId={activeBranchIdForNotebook} /> )} @@ -1149,14 +1188,12 @@ export default function ReasoningWorkspace({ {genuineCompletion && ( )} - {!genuineCompletion && ( - experimentalBranches && experimentalBranches.length > 0 ? ( - // In experiment mode: quiet facilitator state, not evidence limit verdict - - ) : ( - - ) - )} + {!genuineCompletion && experimentalBranches && experimentalBranches.length > 0 ? ( + // Quiet state is handled inside BranchNotebookContent for experiment mode + null + ) : !genuineCompletion ? ( + + ) : null} )} diff --git a/components/scenario-form.jsx b/components/scenario-form.jsx index 20e9980..d2f3864 100644 --- a/components/scenario-form.jsx +++ b/components/scenario-form.jsx @@ -229,6 +229,10 @@ export default function ScenarioForm() { // Pre-seed Competitor development with a late result for RTO.27A testing const [branchNewResults, setBranchNewResults] = useState({ "branch-a": true }); + /* ── RTO.27B — provisional done-for-now state (experimental) ── */ + + const [doneForNowBranchIds, setDoneForNowBranchIds] = useState([]); + /* ── RTO.26B — experimental branch-scoped reasoning fixture ───── */ const branchScoped = useBranchScopedFixture(); @@ -270,15 +274,22 @@ export default function ScenarioForm() { }, [activeBranch, branchScoped]); // Determine which non-active branches have new results for passive indicator + // (RTO.27B: also include done-for-now branches so pause state is visible) const inactiveBranchNewResults = useMemo(() => { const result = {}; BRANCHES.forEach(b => { - if (b.id !== activeBranchId && b.id in branchNewResults) { - result[b.id] = true; + if (b.id !== activeBranchId) { + if (b.id in branchNewResults) { + result[b.id] = true; + } + // Show pause indicator on any done-for-now branch + if (doneForNowBranchIds.includes(b.id)) { + result[b.id] = true; + } } }); return result; - }, [activeBranchId, BRANCHES, branchNewResults]); + }, [activeBranchId, BRANCHES, branchNewResults, doneForNowBranchIds]); /* Restore persisted session on mount (Phase 3) ─────────── */ useEffect(() => { @@ -472,6 +483,17 @@ export default function ScenarioForm() { branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined} branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined} inactiveBranchNewResults={Object.keys(inactiveBranchNewResults).length > 0 ? inactiveBranchNewResults : undefined} + activeBranchIdForNotebook={activeBranchId} + doneForNowBranchIds={doneForNowBranchIds} + onDoneForNow={() => { + if (activeBranchId && !doneForNowBranchIds.includes(activeBranchId)) { + setDoneForNowBranchIds(prev => [...prev, activeBranchId]); + } + }} + onReopenBranch={(id) => { + setDoneForNowBranchIds(prev => prev.filter(x => x !== id)); + setActiveBranchId(id); + }} onRestart={() => { setStatus("idle"); setResult(null); setScenario(""); }} /> ); @@ -484,14 +506,30 @@ export default function ScenarioForm() { branches={BRANCHES} activeBranchId={activeBranchId} branchNewResults={branchNewResults} + branchPauseState={doneForNowBranchIds} onBranchSelect={(id) => { - if (id !== activeBranchId) { - setActiveBranchId(id); + if (id === activeBranchId) return; + // Reopen: if the selected branch is paused, clear its pause state + if (doneForNowBranchIds.includes(id)) { + setDoneForNowBranchIds(prev => prev.filter(x => x !== id)); } + setActiveBranchId(id); }} /> + {/* ── RTO.27B — simulated late update for paused branch (experimental dev control) ─ */} +
+ +
)} @@ -669,6 +707,17 @@ export default function ScenarioForm() { branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined} branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined} inactiveBranchNewResults={Object.keys(inactiveBranchNewResults).length > 0 ? inactiveBranchNewResults : undefined} + activeBranchIdForNotebook={activeBranchId} + doneForNowBranchIds={doneForNowBranchIds} + onDoneForNow={() => { + if (activeBranchId && !doneForNowBranchIds.includes(activeBranchId)) { + setDoneForNowBranchIds(prev => [...prev, activeBranchId]); + } + }} + onReopenBranch={(id) => { + setDoneForNowBranchIds(prev => prev.filter(x => x !== id)); + setActiveBranchId(id); + }} onRestart={() => { clearSession(); setStatus("idle"); @@ -691,11 +740,14 @@ export default function ScenarioForm() { branches={BRANCHES} activeBranchId={activeBranchId} branchNewResults={branchNewResults} + branchPauseState={doneForNowBranchIds} onBranchSelect={(id) => { - /* User intentionally navigates — nothing more */ - if (id !== activeBranchId) { - setActiveBranchId(id); + if (id === activeBranchId) return; + // Reopen: if the selected branch is paused, clear its pause state + if (doneForNowBranchIds.includes(id)) { + setDoneForNowBranchIds(prev => prev.filter(x => x !== id)); } + setActiveBranchId(id); }} />