test(ui): explore provisional branch pause and reopen
This commit is contained in:
@@ -46,7 +46,7 @@ function NewIndicator({ visible }) {
|
|||||||
|
|
||||||
/* ── Single branch row ─────────────────────────────────── */
|
/* ── Single branch row ─────────────────────────────────── */
|
||||||
|
|
||||||
function BranchRow({ id, label, active, isNew, origin, onClick }) {
|
function BranchRow({ id, label, active, isNew, isPaused, origin, onClick }) {
|
||||||
const isActive = Boolean(active);
|
const isActive = Boolean(active);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -80,8 +80,18 @@ function BranchRow({ id, label, active, isNew, origin, onClick }) {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Passive new-result indicator (only for inactive branches) */}
|
{/* Passive indicators: pause + new */}
|
||||||
{!isActive && <NewIndicator visible={isNew} />}
|
<span className="flex items-center gap-1.5 flex-none">
|
||||||
|
{!isActive && isPaused && (
|
||||||
|
<span
|
||||||
|
className="text-[10px] text-gray-400"
|
||||||
|
title="Done for now"
|
||||||
|
>
|
||||||
|
Paused
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!isActive && <NewIndicator visible={isNew} />}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -92,6 +102,7 @@ export default function ExperimentalBranchSwitcher({
|
|||||||
branches = [],
|
branches = [],
|
||||||
activeBranchId,
|
activeBranchId,
|
||||||
branchNewResults = {},
|
branchNewResults = {},
|
||||||
|
branchPauseState = [],
|
||||||
onBranchSelect,
|
onBranchSelect,
|
||||||
}) {
|
}) {
|
||||||
if (!branches.length) return null;
|
if (!branches.length) return null;
|
||||||
@@ -119,6 +130,7 @@ export default function ExperimentalBranchSwitcher({
|
|||||||
label={branch.label}
|
label={branch.label}
|
||||||
active={activeBranchId === branch.id}
|
active={activeBranchId === branch.id}
|
||||||
isNew={Boolean(branchNewResults[branch.id])}
|
isNew={Boolean(branchNewResults[branch.id])}
|
||||||
|
isPaused={branchPauseState.includes(branch.id)}
|
||||||
origin={branch.origin}
|
origin={branch.origin}
|
||||||
onClick={() => onBranchSelect?.(branch.id)}
|
onClick={() => onBranchSelect?.(branch.id)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -351,9 +351,14 @@ function BranchNotebookContent({
|
|||||||
openQuestions,
|
openQuestions,
|
||||||
lateResults,
|
lateResults,
|
||||||
inactiveBranchNewResults,
|
inactiveBranchNewResults,
|
||||||
|
doneForNowBranchIds,
|
||||||
|
onDoneForNow,
|
||||||
|
branchId,
|
||||||
}) {
|
}) {
|
||||||
const hasOpenItems = openQuestions && openQuestions.length > 0;
|
const hasOpenItems = openQuestions && openQuestions.length > 0;
|
||||||
const hasContributions = contributions && contributions.length > 0;
|
const hasContributions = contributions && contributions.length > 0;
|
||||||
|
const isPaused = doneForNowBranchIds?.includes(branchId) || false;
|
||||||
|
const isBranchActive = !isPaused;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -407,9 +412,35 @@ function BranchNotebookContent({
|
|||||||
<UpdatedConnectionCard update={lateResults[0]} />
|
<UpdatedConnectionCard update={lateResults[0]} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 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 && (
|
||||||
|
<div className="flex justify-end pt-1">
|
||||||
|
<button
|
||||||
|
onClick={(e) => { e.stopPropagation(); onDoneForNow(); }}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
className="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-600 hover:bg-gray-50 transition whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Done for now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Quiet state */}
|
||||||
{!hasOpenItems && !lateResults?.length && (
|
{!hasOpenItems && !lateResults?.length && (
|
||||||
<QuietStateCard />
|
<div className="rounded-lg border border-gray-200/60 bg-gray-50/40 px-5 py-5 text-center">
|
||||||
|
{isPaused ? (
|
||||||
|
<>
|
||||||
|
<p className="text-sm leading-relaxed text-gray-500 mb-1">
|
||||||
|
Nothing more to add here right now
|
||||||
|
</p>
|
||||||
|
<p className="text-xs leading-relaxed text-gray-400">
|
||||||
|
This branch is paused. Reasoning preserved — return when you have more information.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<QuietStateCard />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -805,6 +836,11 @@ export default function ReasoningWorkspace({
|
|||||||
branchLocalContributions,
|
branchLocalContributions,
|
||||||
branchLocalLateResults,
|
branchLocalLateResults,
|
||||||
inactiveBranchNewResults,
|
inactiveBranchNewResults,
|
||||||
|
activeBranchIdForNotebook,
|
||||||
|
// ── RTO.27B — provisional pause state ──
|
||||||
|
doneForNowBranchIds,
|
||||||
|
onDoneForNow,
|
||||||
|
onReopenBranch,
|
||||||
}) {
|
}) {
|
||||||
const [investigationHistory, setInvestigationHistory] = useState([]);
|
const [investigationHistory, setInvestigationHistory] = useState([]);
|
||||||
const turnCounter = useRef(0);
|
const turnCounter = useRef(0);
|
||||||
@@ -1115,6 +1151,9 @@ export default function ReasoningWorkspace({
|
|||||||
openQuestions={branchLocalQuestions || []}
|
openQuestions={branchLocalQuestions || []}
|
||||||
lateResults={branchLocalLateResults || []}
|
lateResults={branchLocalLateResults || []}
|
||||||
inactiveBranchNewResults={inactiveBranchNewResults || {}}
|
inactiveBranchNewResults={inactiveBranchNewResults || {}}
|
||||||
|
doneForNowBranchIds={doneForNowBranchIds || []}
|
||||||
|
onDoneForNow={onDoneForNow}
|
||||||
|
branchId={activeBranchIdForNotebook}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1149,14 +1188,12 @@ export default function ReasoningWorkspace({
|
|||||||
{genuineCompletion && (
|
{genuineCompletion && (
|
||||||
<CompletionCard summary={resolveCurrentSummary(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary)} />
|
<CompletionCard summary={resolveCurrentSummary(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary)} />
|
||||||
)}
|
)}
|
||||||
{!genuineCompletion && (
|
{!genuineCompletion && experimentalBranches && experimentalBranches.length > 0 ? (
|
||||||
experimentalBranches && experimentalBranches.length > 0 ? (
|
// Quiet state is handled inside BranchNotebookContent for experiment mode
|
||||||
// In experiment mode: quiet facilitator state, not evidence limit verdict
|
null
|
||||||
<QuietStateCard />
|
) : !genuineCompletion ? (
|
||||||
) : (
|
<EvidenceLimitCard summary={resolveCurrentSummary(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary)} />
|
||||||
<EvidenceLimitCard summary={resolveCurrentSummary(propUnderstanding || graph?.currentSummary || result?.updatedSituationGraph?.currentSummary)} />
|
) : null}
|
||||||
)
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -229,6 +229,10 @@ export default function ScenarioForm() {
|
|||||||
// Pre-seed Competitor development with a late result for RTO.27A testing
|
// Pre-seed Competitor development with a late result for RTO.27A testing
|
||||||
const [branchNewResults, setBranchNewResults] = useState({ "branch-a": true });
|
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 ───── */
|
/* ── RTO.26B — experimental branch-scoped reasoning fixture ───── */
|
||||||
|
|
||||||
const branchScoped = useBranchScopedFixture();
|
const branchScoped = useBranchScopedFixture();
|
||||||
@@ -270,15 +274,22 @@ export default function ScenarioForm() {
|
|||||||
}, [activeBranch, branchScoped]);
|
}, [activeBranch, branchScoped]);
|
||||||
|
|
||||||
// Determine which non-active branches have new results for passive indicator
|
// 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 inactiveBranchNewResults = useMemo(() => {
|
||||||
const result = {};
|
const result = {};
|
||||||
BRANCHES.forEach(b => {
|
BRANCHES.forEach(b => {
|
||||||
if (b.id !== activeBranchId && b.id in branchNewResults) {
|
if (b.id !== activeBranchId) {
|
||||||
result[b.id] = true;
|
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;
|
return result;
|
||||||
}, [activeBranchId, BRANCHES, branchNewResults]);
|
}, [activeBranchId, BRANCHES, branchNewResults, doneForNowBranchIds]);
|
||||||
|
|
||||||
/* Restore persisted session on mount (Phase 3) ─────────── */
|
/* Restore persisted session on mount (Phase 3) ─────────── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -472,6 +483,17 @@ export default function ScenarioForm() {
|
|||||||
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
||||||
branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined}
|
branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined}
|
||||||
inactiveBranchNewResults={Object.keys(inactiveBranchNewResults).length > 0 ? inactiveBranchNewResults : 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(""); }}
|
onRestart={() => { setStatus("idle"); setResult(null); setScenario(""); }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -484,14 +506,30 @@ export default function ScenarioForm() {
|
|||||||
branches={BRANCHES}
|
branches={BRANCHES}
|
||||||
activeBranchId={activeBranchId}
|
activeBranchId={activeBranchId}
|
||||||
branchNewResults={branchNewResults}
|
branchNewResults={branchNewResults}
|
||||||
|
branchPauseState={doneForNowBranchIds}
|
||||||
onBranchSelect={(id) => {
|
onBranchSelect={(id) => {
|
||||||
if (id !== activeBranchId) {
|
if (id === activeBranchId) return;
|
||||||
setActiveBranchId(id);
|
// Reopen: if the selected branch is paused, clear its pause state
|
||||||
|
if (doneForNowBranchIds.includes(id)) {
|
||||||
|
setDoneForNowBranchIds(prev => prev.filter(x => x !== id));
|
||||||
}
|
}
|
||||||
|
setActiveBranchId(id);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* ── RTO.27B — simulated late update for paused branch (experimental dev control) ─ */}
|
||||||
|
<div className="mt-2 flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
// Simulate a late result arriving on the customer-demand branch while paused
|
||||||
|
setBranchNewResults(prev => ({ ...prev, "branch-b": true }));
|
||||||
|
}}
|
||||||
|
className="rounded border border-purple-200/60 bg-purple-50 px-3 py-1.5 text-xs text-purple-700 hover:bg-purple-100 transition"
|
||||||
|
>
|
||||||
|
Simulate late update on Customer demand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -669,6 +707,17 @@ export default function ScenarioForm() {
|
|||||||
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
||||||
branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined}
|
branchLocalLateResults={experimentalBranchLateResults.length > 0 ? experimentalBranchLateResults : undefined}
|
||||||
inactiveBranchNewResults={Object.keys(inactiveBranchNewResults).length > 0 ? inactiveBranchNewResults : 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={() => {
|
onRestart={() => {
|
||||||
clearSession();
|
clearSession();
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
@@ -691,11 +740,14 @@ export default function ScenarioForm() {
|
|||||||
branches={BRANCHES}
|
branches={BRANCHES}
|
||||||
activeBranchId={activeBranchId}
|
activeBranchId={activeBranchId}
|
||||||
branchNewResults={branchNewResults}
|
branchNewResults={branchNewResults}
|
||||||
|
branchPauseState={doneForNowBranchIds}
|
||||||
onBranchSelect={(id) => {
|
onBranchSelect={(id) => {
|
||||||
/* User intentionally navigates — nothing more */
|
if (id === activeBranchId) return;
|
||||||
if (id !== activeBranchId) {
|
// Reopen: if the selected branch is paused, clear its pause state
|
||||||
setActiveBranchId(id);
|
if (doneForNowBranchIds.includes(id)) {
|
||||||
|
setDoneForNowBranchIds(prev => prev.filter(x => x !== id));
|
||||||
}
|
}
|
||||||
|
setActiveBranchId(id);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user