test(ui): make inferred questions originate branches
This commit is contained in:
@@ -362,17 +362,28 @@ function BranchNotebookContent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Why this branch exists */}
|
{/* Why this branch exists — origin question is primary */}
|
||||||
{branchContext && (
|
{branchContext && (
|
||||||
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-5 py-4">
|
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-5 py-4">
|
||||||
<h2 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
<h2 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
Current branch
|
Investigating
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base font-medium leading-tight text-gray-900">{branchContext.label}</p>
|
{branchContext.originQuestion ? (
|
||||||
{branchContext.origin && (
|
<>
|
||||||
<p className="mt-2 text-sm leading-relaxed text-gray-600">
|
<p className="text-base leading-relaxed text-gray-900">{branchContext.originQuestion.text}</p>
|
||||||
Trying to understand: {branchContext.origin}
|
{branchContext.label && branchContext.originQuestion.text !== branchContext.label && (
|
||||||
</p>
|
<p className="mt-2 text-xs text-gray-500/70">Branch: {branchContext.label}</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : branchContext.origin ? (
|
||||||
|
<>
|
||||||
|
<p className="text-base font-medium leading-tight text-gray-900">{branchContext.label}</p>
|
||||||
|
<p className="mt-2 text-sm leading-relaxed text-gray-600">
|
||||||
|
Trying to understand: {branchContext.origin}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="text-base font-medium leading-tight text-gray-900">{branchContext.label}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -237,8 +237,8 @@ export function derivePrimarySurface(result, status, showExperimentView, scenari
|
|||||||
|
|
||||||
const valid = hasValidInvestigationContext(result, status, scenario);
|
const valid = hasValidInvestigationContext(result, status, scenario);
|
||||||
|
|
||||||
// RTO.28A: show branch-selection surface when investigation exists but no branch is active
|
// RTO.28B: show question-selection surface when investigation exists but no question/branch is active
|
||||||
if (showExperimentView && valid && !activeBranchId) return "BRANCH_SELECTION";
|
if (showExperimentView && valid && !activeBranchId) return "QUESTION_SELECTION";
|
||||||
|
|
||||||
if (showExperimentView && valid) return "EXPERIMENT_NOTEBOOK";
|
if (showExperimentView && valid) return "EXPERIMENT_NOTEBOOK";
|
||||||
if (!showExperimentView && valid) return "NORMAL_WORKSPACE";
|
if (!showExperimentView && valid) return "NORMAL_WORKSPACE";
|
||||||
@@ -283,6 +283,15 @@ export default function ScenarioForm() {
|
|||||||
// Use fixture branches as the authoritative source when available
|
// Use fixture branches as the authoritative source when available
|
||||||
const BRANCHES = useMemo(() => branchScoped.getBranches(), [branchScoped]);
|
const BRANCHES = useMemo(() => branchScoped.getBranches(), [branchScoped]);
|
||||||
|
|
||||||
|
// Track the originating question for provenance
|
||||||
|
const [originQuestion, setOriginQuestion] = useState(null);
|
||||||
|
|
||||||
|
// Extract inferred questions from the fixture (flat list for post-Analyse surface)
|
||||||
|
const inferredQuestions = useMemo(() => {
|
||||||
|
if (!branchScoped || typeof branchScoped.getAllInferredQuestions !== 'function') return [];
|
||||||
|
return branchScoped.getAllInferredQuestions();
|
||||||
|
}, [branchScoped]);
|
||||||
|
|
||||||
// Simulate a late semantic result arriving on Branch A ~2s after a graph loads
|
// Simulate a late semantic result arriving on Branch A ~2s after a graph loads
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status !== "success" && status !== "error") return;
|
if (status !== "success" && status !== "error") return;
|
||||||
@@ -538,7 +547,7 @@ export default function ScenarioForm() {
|
|||||||
<>
|
<>
|
||||||
<PulseStyle />
|
<PulseStyle />
|
||||||
{!activeBranchId ? (
|
{!activeBranchId ? (
|
||||||
/* RTO.28A: no active branch — show branch-selection surface */
|
/* RTO.28B: inferred questions surface — user chooses what to investigate */
|
||||||
<div className="max-w-xl mx-auto space-y-6">
|
<div className="max-w-xl mx-auto space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Situation remains visible */}
|
{/* Situation remains visible */}
|
||||||
@@ -552,25 +561,30 @@ export default function ScenarioForm() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Branch selection */}
|
{/* Inferred questions — user chooses what to investigate */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xs font-medium text-gray-700">Choose a branch to continue</h2>
|
<h2 className="text-xs font-medium text-gray-700">Questions to explore</h2>
|
||||||
<p className="text-sm text-gray-500">These are the current lines of inquiry. Pick whichever you want to work on.</p>
|
<p className="text-sm text-gray-500">Click a question to start investigating. A branch will be created for your choice.</p>
|
||||||
|
|
||||||
{BRANCHES.map((branch) => (
|
{inferredQuestions.map((q) => (
|
||||||
<button
|
<button
|
||||||
key={branch.id}
|
key={q.id}
|
||||||
onClick={() => setActiveBranchId(branch.id)}
|
onClick={() => {
|
||||||
|
const targetBranch = BRANCHES.find(b => b.id === q.branchId);
|
||||||
|
if (targetBranch) {
|
||||||
|
setActiveBranchId(targetBranch.id);
|
||||||
|
setOriginQuestion({ id: q.id, text: q.text });
|
||||||
|
}
|
||||||
|
}}
|
||||||
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"
|
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 font-medium text-sm text-gray-900">{branch.label}</span>
|
<span className="block text-sm leading-relaxed text-gray-900">{q.text}</span>
|
||||||
{branch.origin && (
|
|
||||||
<span className="block mt-1 text-xs leading-relaxed text-gray-500/80">
|
|
||||||
{branch.origin}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{inferredQuestions.length === 0 && (
|
||||||
|
<p className="text-sm text-gray-400">No questions available yet.</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -592,7 +606,7 @@ export default function ScenarioForm() {
|
|||||||
setAnswer={() => {}}
|
setAnswer={() => {}}
|
||||||
onAnswerSubmit={async (e) => e.preventDefault()}
|
onAnswerSubmit={async (e) => e.preventDefault()}
|
||||||
lastSubmittedAnswer=""
|
lastSubmittedAnswer=""
|
||||||
branchContext={branchContext}
|
branchContext={{ ...branchContext, originQuestion: originQuestion }}
|
||||||
experimentalBranches={BRANCHES.length > 0 ? BRANCHES : undefined}
|
experimentalBranches={BRANCHES.length > 0 ? BRANCHES : undefined}
|
||||||
branchLocalQuestions={experimentalBranchQuestions.length > 0 ? experimentalBranchQuestions : undefined}
|
branchLocalQuestions={experimentalBranchQuestions.length > 0 ? experimentalBranchQuestions : undefined}
|
||||||
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
branchLocalContributions={experimentalBranchContributions.length > 0 ? experimentalBranchContributions : undefined}
|
||||||
|
|||||||
@@ -190,6 +190,21 @@ export function useBranchScopedFixture() {
|
|||||||
return Object.values(BRANCH_META).map(m => ({ ...m }));
|
return Object.values(BRANCH_META).map(m => ({ ...m }));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Return all inferred questions across branches (flat list for post-Analyse surface). */
|
||||||
|
getAllInferredQuestions() {
|
||||||
|
const qA1 = this._qA1;
|
||||||
|
const qA2 = this._qA2;
|
||||||
|
const qB1 = this._qB1;
|
||||||
|
const qB2 = this._qB2;
|
||||||
|
if (!qA1 || !qA2 || !qB1 || !qB2) return [];
|
||||||
|
return [
|
||||||
|
{ id: qA1.id, text: qA1.text, branchId: "branch-competitor-development" },
|
||||||
|
{ id: qA2.id, text: qA2.text, branchId: "branch-competitor-development" },
|
||||||
|
{ id: qB1.id, text: qB1.text, branchId: "branch-customer-demand" },
|
||||||
|
{ id: qB2.id, text: qB2.text, branchId: "branch-customer-demand" },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
/** Return all records (for verification / debugging). */
|
/** Return all records (for verification / debugging). */
|
||||||
getAllRecords() {
|
getAllRecords() {
|
||||||
return allRecords;
|
return allRecords;
|
||||||
|
|||||||
Reference in New Issue
Block a user