+ {/* RTO.26B — situation card from scenario text when no graph */}
+ {!graph && scenario && (
+
}
{/* RTO.25B — temporarily hidden to reduce competing navigation while branch-experiment is active */}
diff --git a/components/scenario-form.jsx b/components/scenario-form.jsx
index 4f37ea8..29e9290 100644
--- a/components/scenario-form.jsx
+++ b/components/scenario-form.jsx
@@ -6,6 +6,7 @@ import DiagnosticsView from "@/components/diagnostics-view";
import ReasoningWorkspace, { LoadingOverlay, ContinueLaterBanner } from "@/components/reasoning-workspace";
import ExperimentalBranchSwitcher, { PulseStyle } from "@/components/experimental/branch-switcher";
import { mockFetch, AVAILABLE_SCENARIOS } from "@/lib/mocks/confidence-engine/mock-client";
+import { useBranchScopedFixture } from "@/lib/fixtures/rto26b-branch-scoped.mjs";
/* Compile-time env resolution — NEXT_PUBLIC_ vars are injected by Next.js at build */
const MOCK_ENABLED = process.env.NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS === "true";
@@ -224,26 +225,23 @@ export default function ScenarioForm() {
/* ── RTO.25A — passive late-result branch switcher (experimental) ── */
- const BRANCHES = [
- {
- id: "branch-a",
- label: "Competitor development",
- origin: "Whether competitors are developing similar products and when they might release them",
- },
- {
- id: "branch-b",
- label: "Customer demand",
- origin: "How many customers will buy the product, and what revenue that represents",
- },
- ];
-
const [activeBranchId, setActiveBranchId] = useState("branch-b");
const [branchNewResults, setBranchNewResults] = useState({});
+ /* ── RTO.26B — experimental branch-scoped reasoning fixture ───── */
+
+ const branchScoped = useBranchScopedFixture();
+ // Toggle to control when the experiment view is visible vs production
+ const [showExperimentView, setShowExperimentView] = useState(
+ typeof window !== "undefined" ? (window.sessionStorage?.getItem("ce-show-experiment") !== "false") : true,
+ );
+
+ // Use fixture branches as the authoritative source when available
+ const BRANCHES = useMemo(() => branchScoped.getBranches(), [branchScoped]);
+
// Simulate a late semantic result arriving on Branch A ~2s after a graph loads
useEffect(() => {
if (status !== "success" && status !== "error") return;
- // Only simulate once per graph load
if (branchNewResults["branch-a"]) return;
const timer = setTimeout(() => {
@@ -252,6 +250,29 @@ export default function ScenarioForm() {
return () => clearTimeout(timer);
}, [status, branchNewResults]);
+ // Compute branch-local data for the active branch (explicit provenance)
+ const activeBranch = BRANCHES.find(b => b.id === activeBranchId);
+ const experimentalBranchQuestions = useMemo(() => {
+ if (!activeBranch) return [];
+ return branchScoped.getBranchQuestions(activeBranch.id);
+ }, [activeBranch, branchScoped]);
+
+ const experimentalBranchContributions = useMemo(() => {
+ if (!activeBranch) return [];
+ return branchScoped.getBranchContributions(activeBranch.id);
+ }, [activeBranch, branchScoped]);
+
+ // Determine which non-active branches have new results for passive indicator
+ const inactiveBranchNewResults = useMemo(() => {
+ const result = {};
+ BRANCHES.forEach(b => {
+ if (b.id !== activeBranchId && b.id in branchNewResults) {
+ result[b.id] = true;
+ }
+ });
+ return result;
+ }, [activeBranchId, BRANCHES, branchNewResults]);
+
/* Restore persisted session on mount (Phase 3) ─────────── */
useEffect(() => {
if (typeof window === "undefined") return;
@@ -417,7 +438,75 @@ export default function ScenarioForm() {
return (
- {status === "idle" && (
+ {/* ── RTO.26B — standalone branch-scoped experimental view (shown when experiment is active) ───── */}
+ {showExperimentView && status !== "loading" && !result?.situationGraph && !result?.updatedSituationGraph && (
+ <>
+
+
+ {/* Workspace (3/4) — branch-scoped fixture only, no API needed */}
+
+ {(() => {
+ const activeBranch = BRANCHES.find(b => b.id === activeBranchId);
+ const branchContext = activeBranch ? { label: activeBranch.label, origin: activeBranch.origin } : null;
+ return (
+ {}}
+ onAnswerSubmit={async (e) => e.preventDefault()}
+ lastSubmittedAnswer=""
+ branchContext={branchContext}
+ experimentalBranches={BRANCHES.length > 0 ? BRANCHES : undefined}
+ branchLocalQuestions={experimentalBranchQuestions.length > 0 ? experimentalBranchQuestions : undefined}
+ branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
+ inactiveBranchNewResults={Object.keys(inactiveBranchNewResults).length > 0 ? inactiveBranchNewResults : undefined}
+ onRestart={() => { setStatus("idle"); setResult(null); setScenario(""); }}
+ />
+ );
+ })()}
+
+
+ {/* Branch switcher (1/4 sidebar) */}
+
+ {
+ if (id !== activeBranchId) {
+ setActiveBranchId(id);
+ }
+ }}
+ />
+
+
+ >
+ )}
+
+ {/* ── Experiment toggle (visible when experiment is NOT shown) ─ */}
+ {!showExperimentView && status !== "loading" && !result?.updatedSituationGraph && (
+
+
+ Production view active.
+
+
{
+ setShowExperimentView(true);
+ try { window.sessionStorage?.setItem("ce-show-experiment", "true"); } catch {}
+ }}
+ className="rounded-lg border border-blue-600 bg-white px-4 py-2 text-sm font-medium text-blue-700 hover:bg-blue-50 transition"
+ >
+ Try branch-scoped experiment (RTO.26B)
+
+
+ )}
+
+ {/* ── Idle form for scenario input (shown only when experiment is off) ─ */}
+ {!showExperimentView && !result?.situationGraph && status === "idle" && (
@@ -459,7 +548,7 @@ export default function ScenarioForm() {
{/* Right panel — Workspace (2/3 on desktop) */}