Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00ba343ed9 | ||
|
|
8c98ce94de | ||
|
|
bdb234262c | ||
|
|
07e1363368 | ||
|
|
16cab4645a | ||
|
|
922f58a49f | ||
|
|
bf7629691f | ||
|
|
ae1201bb27 | ||
|
|
8bded90094 | ||
|
|
17c6048047 | ||
|
|
890a18c5a7 | ||
|
|
50a66749ae | ||
|
|
88d9768276 | ||
|
|
dac19a3552 | ||
|
|
b9c0b6f6f7 | ||
|
|
0f4dfcbb17 | ||
|
|
a8539e2494 | ||
|
|
06f3f501d2 | ||
|
|
10cbcbdd05 | ||
|
|
b9a54589f0 | ||
|
|
556acfb156 | ||
|
|
f1bd91faf8 | ||
|
|
e221bd3bf8 | ||
|
|
c45b703b3a | ||
|
|
b215846478 | ||
|
|
3e9123fe0e | ||
|
|
3c5257cbd7 | ||
|
|
d55f179d37 | ||
|
|
166ee91698 | ||
|
|
bc35e05253 | ||
|
|
ba956eeb3a | ||
|
|
22e1d7484b | ||
|
|
5c926154cd | ||
|
|
bf6c4241a5 | ||
|
|
0f4e49fc6c | ||
|
|
7858650334 | ||
|
|
ca7d8e5384 | ||
|
|
0659599795 | ||
|
|
dc558e9c37 | ||
|
|
061ea364b3 | ||
|
|
6a5cb43a30 | ||
|
|
abeb3fcb03 | ||
|
|
990b51aecf | ||
|
|
48b7185176 | ||
|
|
3235c35cf0 | ||
|
|
d3015f63d8 | ||
|
|
854160726e | ||
|
|
10aa18d367 | ||
|
|
ac5fbe7896 | ||
|
|
b26ea7d0ba | ||
|
|
cb707c0192 | ||
|
|
787c8114ad | ||
|
|
fdb173d0e9 | ||
|
|
cfd463d8b3 | ||
|
|
fd02be0f29 | ||
|
|
6288ef1031 | ||
|
|
81dda77392 | ||
|
|
42a7e82d88 | ||
|
|
3ca37b0918 | ||
|
|
112739b8e5 | ||
|
|
9d670822a3 | ||
|
|
2c108df5a9 | ||
|
|
c134b5cb04 | ||
|
|
01c57788ee | ||
|
|
96ad0e7915 | ||
|
|
517d780e2c | ||
|
|
68be2344c6 | ||
|
|
fce68a050f | ||
|
|
41afd9b49f | ||
|
|
c9335cf850 | ||
|
|
86287bebe8 | ||
|
|
412551c968 | ||
|
|
4b264c5681 | ||
|
|
65ced2e406 | ||
|
|
4761d07a76 | ||
|
|
173240d76c | ||
|
|
cef8f46bd6 | ||
|
|
86bb3426ef | ||
|
|
df0e3b5a9b | ||
|
|
e7a1bc689c | ||
|
|
36060faf16 | ||
|
|
49c4b904df | ||
|
|
09eeed5a9e | ||
|
|
47cd0c7d7b | ||
|
|
6bf9e7e710 | ||
|
|
8163c5d014 | ||
|
|
0ac2e05c40 | ||
|
|
ad42f67800 | ||
|
|
058ad2326f | ||
|
|
9fb9735c6e | ||
|
|
a00adfabf4 | ||
|
|
601e46e4b7 | ||
|
|
85204f96ac | ||
|
|
e1a18e27e7 | ||
|
|
a73f125f8d | ||
|
|
c97f07ba65 | ||
|
|
67103fa8d4 | ||
|
|
4687226bd8 | ||
|
|
8fb284c374 | ||
|
|
8c52939c02 | ||
|
|
644108db71 | ||
|
|
0b0d5594fe | ||
|
|
fbeaf01f90 | ||
|
|
e6d0327641 | ||
|
|
a12f9555af | ||
|
|
5b43c1b8f9 | ||
|
|
e1b54e4073 | ||
|
|
6ed3415220 | ||
|
|
98889039c2 | ||
|
|
9c715161b0 | ||
|
|
56de4a7ef3 | ||
|
|
b20707c447 | ||
|
|
6ed4d60029 | ||
|
|
153bbee85e | ||
|
|
8520f2195d | ||
|
|
ebcf1d9306 | ||
|
|
dafc020f66 | ||
|
|
7add85d8d2 | ||
|
|
c0b963973f | ||
|
|
913dfec507 | ||
|
|
952cb442b5 | ||
|
|
2f6c90b027 | ||
|
|
648e1c7a29 | ||
|
|
fd98cda8ba | ||
|
|
6b25100f9a | ||
|
|
db5016c138 | ||
|
|
4a34dcc361 | ||
|
|
25a88c5fc3 |
+37
-10
@@ -19,6 +19,18 @@ It:
|
|||||||
6. updates the graph from the answer;
|
6. updates the graph from the answer;
|
||||||
7. repeats until action is justified or the remaining uncertainty is clear.
|
7. repeats until action is justified or the remaining uncertainty is clear.
|
||||||
|
|
||||||
|
> **NOTE:** The flow above describes historical/current implementation mechanics.
|
||||||
|
> It does not represent current Confidence Engine methodology direction.
|
||||||
|
> See `docs/Confidence_Engine_Return_to_Origin_Methodology_Context_2026-08-18.md`
|
||||||
|
> for the current working hypothesis (granular answer-fragment inquiry).
|
||||||
|
|
||||||
|
The linear selector-led flow described above is a **historical capability**, not
|
||||||
|
an automatic architecture to continue. Under Return-to-Origin:
|
||||||
|
|
||||||
|
- The Engine facilitates inquiry; it does not compel a single-question route.
|
||||||
|
- The user owns which unresolved investigation/question to pursue.
|
||||||
|
- Accumulated reasoning memory does not necessarily belong inside repeated LLM calls.
|
||||||
|
|
||||||
A chatbot remembers the conversation.
|
A chatbot remembers the conversation.
|
||||||
|
|
||||||
The Confidence Engine preserves the state of the reasoning.
|
The Confidence Engine preserves the state of the reasoning.
|
||||||
@@ -50,15 +62,23 @@ The engine should help a user reach one of these states:
|
|||||||
|
|
||||||
## Current development stage
|
## Current development stage
|
||||||
|
|
||||||
The deterministic reasoning architecture reached a stable alpha checkpoint.
|
> **Version lineage note:** The Confidence Engine uses two distinct version
|
||||||
|
> lineages that must not be conflated:
|
||||||
|
> - **Reasoning-engine experimental lineage** (v0.8+): reasoning-fidelity,
|
||||||
|
> investigation-state assessment, semantic selectors — under RTO pause.
|
||||||
|
> - **UX/product development lineage** (v0.7): workspace layout, user views,
|
||||||
|
> loading feedback — also paused.
|
||||||
|
> These are independent tracks; do not assume they describe one product version.
|
||||||
|
|
||||||
Current work is primarily improving:
|
The deterministic reasoning architecture reached a stable alpha checkpoint
|
||||||
|
(reasoning-engine v0.8). UI/product work reached v0.7 staging. Both have
|
||||||
|
paused under Return to Origin while the granular answer-fragment hypothesis
|
||||||
|
is evaluated as working methodology context.
|
||||||
|
|
||||||
- usability;
|
Current work is paused. The next step begins from the methodology question:
|
||||||
- presentation;
|
given the useful investigation structure the Engine can already derive, how
|
||||||
- loading feedback;
|
should that structure be surfaced so a person can see, choose, defer, and
|
||||||
- plain-language explanations;
|
return to open questions while the Engine continues to guide their thinking?
|
||||||
- separation of user and developer views.
|
|
||||||
|
|
||||||
Do not resume broad reasoning architecture work unless a repeated observed
|
Do not resume broad reasoning architecture work unless a repeated observed
|
||||||
failure clearly requires it.
|
failure clearly requires it.
|
||||||
@@ -93,7 +113,11 @@ The interface should minimise cognitive load by presenting the current state fir
|
|||||||
|
|
||||||
The engine may contain hundreds of reasoning nodes; the user should only see the information required to take the next meaningful action.
|
The engine may contain hundreds of reasoning nodes; the user should only see the information required to take the next meaningful action.
|
||||||
|
|
||||||
## Why workspace layout matters (v0.7)
|
## Why workspace layout matters (v0.7 — UX/product lineage)
|
||||||
|
|
||||||
|
> **This section documents paused UX design intent.** It belongs to the v0.7
|
||||||
|
> product development lineage, not the reasoning-engine lineage. UI work is
|
||||||
|
> currently paused under Return to Origin.
|
||||||
|
|
||||||
This phase optimises for simultaneous visibility instead of sequential scrolling.
|
This phase optimises for simultaneous visibility instead of sequential scrolling.
|
||||||
Related panels — Understanding alongside Investigation Map, Situation alongside History — can appear side-by-side on wide screens while mobile continues to stack everything vertically. The reasoning engine is completely unaware of these changes; only the presentation layer is affected.
|
Related panels — Understanding alongside Investigation Map, Situation alongside History — can appear side-by-side on wide screens while mobile continues to stack everything vertically. The reasoning engine is completely unaware of these changes; only the presentation layer is affected.
|
||||||
@@ -104,7 +128,10 @@ Read `docs/current-working-principles.md` for current guidance. Treat `docs/arch
|
|||||||
|
|
||||||
For UI mock work, read `docs/ui-mock-reference.md`. Do not load
|
For UI mock work, read `docs/ui-mock-reference.md`. Do not load
|
||||||
`docs/archive/deferred-ux-backlog.md` unless a named past UX idea is being reviewed.
|
`docs/archive/deferred-ux-backlog.md` unless a named past UX idea is being reviewed.
|
||||||
Engine and UI experiments are paused. First file to inspect when resuming:
|
|
||||||
`docs/current-project-state.md`, then `docs/project-knowledge-inventory.md`.
|
Engine and UI experiments are paused under Return to Origin. First file to inspect when resuming:
|
||||||
|
**`docs/current-handoff.md`** (methodology continuity anchor), then `docs/current-project-state.md`, then `docs/project-knowledge-inventory.md`.
|
||||||
|
|
||||||
> After reading `docs/current-project-state.md`, choose the relevant minimal pack from `docs/task-context-packs.md`. Do not combine packs unless a specific task genuinely crosses boundaries.
|
> After reading `docs/current-project-state.md`, choose the relevant minimal pack from `docs/task-context-packs.md`. Do not combine packs unless a specific task genuinely crosses boundaries.
|
||||||
|
>
|
||||||
|
> **Historical experiment families are evidence to load only when a specific question requires them; they are not default architecture context.**
|
||||||
|
|||||||
@@ -515,6 +515,21 @@ Three tiers, applied top to bottom:
|
|||||||
- Omit items too verbose to scan; do not synthesise rewritten claims.
|
- Omit items too verbose to scan; do not synthesise rewritten claims.
|
||||||
- Never invent facts absent from the graph.
|
- Never invent facts absent from the graph.
|
||||||
|
|
||||||
|
### Provenance and attribution
|
||||||
|
|
||||||
|
Preserve authorship and provenance in every user-facing presentation.
|
||||||
|
|
||||||
|
When displaying a user's previous input, keep it visibly distinct from system-generated interpretation. If the original user wording is available, present it as the user's response rather than rewriting it into system prose. Derived Findings, summaries, uncertainties, assumptions, or follow-up questions must not be styled or worded in a way that implies the user said them.
|
||||||
|
|
||||||
|
The distinction should be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
User response → user-authored (verbatim)
|
||||||
|
What we learned → Engine-derived
|
||||||
|
```
|
||||||
|
|
||||||
|
Exact labels are subject to UX refinement; the durable rule is separating provenance, not prescribing specific copy.
|
||||||
|
|
||||||
## Investigation Narrative
|
## Investigation Narrative
|
||||||
|
|
||||||
The reasoning graph is the machine representation of the investigation.
|
The reasoning graph is the machine representation of the investigation.
|
||||||
|
|||||||
@@ -39,3 +39,10 @@ yarn-error.log*
|
|||||||
evaluation-results/
|
evaluation-results/
|
||||||
provider-debug-results/
|
provider-debug-results/
|
||||||
tests-results/
|
tests-results/
|
||||||
|
|
||||||
|
# Local Playwright MCP runtime output
|
||||||
|
.playwright-mcp/
|
||||||
|
|
||||||
|
|
||||||
|
# Evidence/temp directories from live experiments
|
||||||
|
.evidence-temp/
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { getProvider } from "@/lib/llm/provider";
|
||||||
|
import { buildFocusedDeconstructPrompt, validateFocusedDeconstructSchema } from "@/lib/graph/focused-investigation";
|
||||||
|
|
||||||
|
export async function POST(request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
if (!body.targetNodeId || typeof body.targetNodeId !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'targetNodeId' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.targetLabel || typeof body.targetLabel !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'targetLabel' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.targetDescription || typeof body.targetDescription !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'targetDescription' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.centralStatement || typeof body.centralStatement !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'centralStatement' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.question || typeof body.question !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'question' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.answer || typeof body.answer !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include an 'answer' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = buildFocusedDeconstructPrompt({
|
||||||
|
targetLabel: body.targetLabel,
|
||||||
|
targetDescription: body.targetDescription,
|
||||||
|
centralStatement: body.centralStatement,
|
||||||
|
question: body.question,
|
||||||
|
answer: body.answer,
|
||||||
|
});
|
||||||
|
|
||||||
|
const provider = getProvider();
|
||||||
|
const startedAt = Date.now();
|
||||||
|
const raw = await provider.generateReconstruction(prompt, process.env.OLLAMA_MODEL);
|
||||||
|
const elapsedMs = Date.now() - startedAt;
|
||||||
|
|
||||||
|
// Validate schema (required fields present, no graph-mutation fields)
|
||||||
|
const validationErrors = validateFocusedDeconstructSchema(raw);
|
||||||
|
if (validationErrors.length > 0) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: "Focused deconstruction result did not match expected schema",
|
||||||
|
validationErrors,
|
||||||
|
targetNodeId: body.targetNodeId,
|
||||||
|
elapsedMs,
|
||||||
|
},
|
||||||
|
{ status: 502 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
targetNodeId: body.targetNodeId,
|
||||||
|
observations: raw.observations,
|
||||||
|
uncertainties: raw.uncertainties,
|
||||||
|
assumptions: raw.assumptions,
|
||||||
|
relationships: raw.relationships,
|
||||||
|
possibleFollowUpQuestions: raw.possibleFollowUpQuestions,
|
||||||
|
elapsedMs,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return Response.json(
|
||||||
|
{ error: e.message || "Unknown server error" },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { formulateQuestionForTarget } from "@/lib/graph/focused-investigation";
|
||||||
|
|
||||||
|
export async function POST(request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
if (!body.targetNodeId || typeof body.targetNodeId !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'targetNodeId' string field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body.situationGraph || typeof body.situationGraph !== "object") {
|
||||||
|
return Response.json(
|
||||||
|
{ error: "Request must include a 'situationGraph' object field" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = formulateQuestionForTarget({
|
||||||
|
situationGraph: body.situationGraph,
|
||||||
|
targetNodeId: body.targetNodeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return Response.json(
|
||||||
|
{ success: false, error: result.error },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
targetNodeId: result.targetNodeId,
|
||||||
|
question: result.question,
|
||||||
|
strategy: result.strategy,
|
||||||
|
reasoningPattern: result.reasoningPattern,
|
||||||
|
reasoningPatternReason: result.reasoningPatternReason,
|
||||||
|
reason: result.reason,
|
||||||
|
questionFamily: result.questionFamily,
|
||||||
|
selectedQuestionTemplate: result.selectedQuestionTemplate,
|
||||||
|
allowedQuestionFamilies: result.allowedQuestionFamilies,
|
||||||
|
rejectedQuestionFamilies: result.rejectedQuestionFamilies,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return Response.json(
|
||||||
|
{ error: e.message || "Unknown server error" },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* Experimental branch switcher — RTO.25A
|
||||||
|
*
|
||||||
|
* Smallest branch representation needed to test passive late-result indication.
|
||||||
|
* Does NOT replace production branch navigation. Temporary fixture only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
/* ── Keyframes (injected once via <style> at render) ───── */
|
||||||
|
|
||||||
|
const PulseStyle = () => (
|
||||||
|
<style>{`
|
||||||
|
@keyframes rto-pulse {
|
||||||
|
0%, 100% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
);
|
||||||
|
|
||||||
|
/* ── Status dot (passive new-result indicator) ─────────── */
|
||||||
|
|
||||||
|
function NewIndicator({ visible }) {
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="ml-2 inline-flex items-center"
|
||||||
|
title="Something new is available here"
|
||||||
|
aria-label="New result available"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="relative inline-block h-[8px] w-[8px]"
|
||||||
|
style={{ animation: "rto-pulse 3s ease-in-out infinite" }}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="absolute inset-0 rounded-full bg-blue-400/70"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Single branch row ─────────────────────────────────── */
|
||||||
|
|
||||||
|
function BranchRow({ id, label, active, isNew, isPaused, origin, onClick }) {
|
||||||
|
const isActive = Boolean(active);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={isActive}
|
||||||
|
aria-current={isActive ? "page" : undefined}
|
||||||
|
className={`w-full flex items-start gap-2 rounded-md px-3 py-2 text-left transition text-sm ${
|
||||||
|
isActive
|
||||||
|
? "bg-blue-50/80 border border-blue-200/60 text-blue-900 font-medium"
|
||||||
|
: "text-gray-600 hover:bg-gray-100/70 hover:text-gray-800 border border-transparent"
|
||||||
|
} ${!isActive ? "cursor-pointer" : "cursor-default"}`}
|
||||||
|
>
|
||||||
|
{/* Active indicator — ● vs ○ */}
|
||||||
|
<span
|
||||||
|
className={`flex-none leading-none text-base ${
|
||||||
|
isActive ? "text-blue-500" : "text-gray-400"
|
||||||
|
}`}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{isActive ? "●" : "○"}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Branch label + origin */}
|
||||||
|
<span className="flex-1 min-w-0">
|
||||||
|
<span className="truncate block">{label}</span>
|
||||||
|
{origin && (
|
||||||
|
<span className="block text-[11px] leading-tight text-gray-500/80 truncate" title={origin}>
|
||||||
|
{origin}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Passive indicators: pause + new */}
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Card wrapper ────────────────────────────────────────── */
|
||||||
|
|
||||||
|
export default function ExperimentalBranchSwitcher({
|
||||||
|
branches = [],
|
||||||
|
activeBranchId,
|
||||||
|
branchNewResults = {},
|
||||||
|
branchPauseState = [],
|
||||||
|
onBranchSelect,
|
||||||
|
}) {
|
||||||
|
if (!branches.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="rounded-lg border border-gray-200/60 bg-gray-50/30 p-4"
|
||||||
|
role="radiogroup"
|
||||||
|
aria-label="Experimental branch switcher — RTO.25A"
|
||||||
|
>
|
||||||
|
{/* Label — clearly experimental */}
|
||||||
|
<h2 className="mb-1 text-[10px] font-semibold tracking-widest uppercase text-gray-600">
|
||||||
|
Branches{" "}
|
||||||
|
<span className="font-normal text-gray-500">(exp)</span>
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3 text-[11px] font-medium leading-tight text-gray-500/80">
|
||||||
|
Browse branches. Current focus is preserved.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-1" role="list" aria-label="Available branches">
|
||||||
|
{branches.map((branch) => (
|
||||||
|
<BranchRow
|
||||||
|
key={branch.id}
|
||||||
|
id={branch.id}
|
||||||
|
label={branch.label}
|
||||||
|
active={activeBranchId === branch.id}
|
||||||
|
isNew={Boolean(branchNewResults[branch.id])}
|
||||||
|
isPaused={branchPauseState.includes(branch.id)}
|
||||||
|
origin={branch.origin}
|
||||||
|
onClick={() => onBranchSelect?.(branch.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { PulseStyle };
|
||||||
@@ -84,10 +84,10 @@ export default function InvestigationMap({ turnCount = 0 }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-gray-200/60 bg-gray-50/30 p-4" role="region" aria-label="Investigation map preview">
|
<div className="rounded-lg border border-gray-200/60 bg-gray-50/30 p-4" role="region" aria-label="Investigation map preview">
|
||||||
<h2 className="mb-1 text-[11px] font-medium tracking-widest uppercase text-gray-300">
|
<h2 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
Investigation Map
|
Investigation Map
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mb-3 text-xs text-gray-400/70">
|
<p className="mb-3 text-xs font-medium leading-tight text-gray-500/80">
|
||||||
Active investigation topics and their status.
|
Active investigation topics and their status.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ function InvestigationSummaryPanelV2({ graph, selectedQuestion, result, updateSt
|
|||||||
<div>
|
<div>
|
||||||
{stillInvestigating.length > 1 ? (
|
{stillInvestigating.length > 1 ? (
|
||||||
<>
|
<>
|
||||||
<h3 className="mb-2 text-xs font-medium text-gray-400">Still investigating</h3>
|
<h3 className="mb-2 text-xs font-medium text-gray-500">Still investigating</h3>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
{Object.entries(investigatingByGroup).map(([group, items]) => (
|
{Object.entries(investigatingByGroup).map(([group, items]) => (
|
||||||
<li key={group}>
|
<li key={group}>
|
||||||
@@ -227,7 +227,7 @@ function InvestigationSummaryPanelV2({ graph, selectedQuestion, result, updateSt
|
|||||||
{/* ── What we have learned ────────────────────────── */}
|
{/* ── What we have learned ────────────────────────── */}
|
||||||
{known.length > 0 && (
|
{known.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-xs font-medium text-gray-400">What we know</h3>
|
<h3 className="mb-2 text-xs font-medium text-gray-500">What we know</h3>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
{known.map((item, i) => (
|
{known.map((item, i) => (
|
||||||
<li key={i} className="flex items-start gap-2">
|
<li key={i} className="flex items-start gap-2">
|
||||||
@@ -243,8 +243,8 @@ function InvestigationSummaryPanelV2({ graph, selectedQuestion, result, updateSt
|
|||||||
|
|
||||||
{/* ── Quiet reasoning summary — secondary ─────────── */}
|
{/* ── Quiet reasoning summary — secondary ─────────── */}
|
||||||
<div className="pt-2 border-t border-gray-200/40">
|
<div className="pt-2 border-t border-gray-200/40">
|
||||||
<p className="text-[10px] font-medium tracking-widest uppercase text-gray-300 mb-1.5">Reasoning</p>
|
<p className="text-[10px] font-semibold tracking-widest uppercase text-gray-500 mb-1.5">Reasoning</p>
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-gray-400">
|
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-gray-500">
|
||||||
{reasonEntries.map(([label, count]) => (
|
{reasonEntries.map(([label, count]) => (
|
||||||
<span key={label}>
|
<span key={label}>
|
||||||
{count} {label}
|
{count} {label}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function KnownSection({ title, items }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-400">
|
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-500">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
@@ -67,7 +67,7 @@ function InvestigatingSection({ title, items }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-400">
|
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-500">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
@@ -87,7 +87,7 @@ function ExplanationSection({ items }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-400">
|
<h3 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-500">
|
||||||
Possible explanations
|
Possible explanations
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
@@ -102,10 +102,10 @@ function QuietSummary({ text }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pt-2 border-t border-gray-200/40">
|
<div className="pt-2 border-t border-gray-200/40">
|
||||||
<p className="text-[10px] font-medium tracking-widest uppercase text-gray-300 mb-1.5">
|
<p className="text-[10px] font-semibold tracking-widest uppercase text-gray-500 mb-1.5">
|
||||||
Investigation state
|
Investigation state
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-400">{text}</p>
|
<p className="text-xs text-gray-500">{text}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ function InvestigationSummaryPanel({ graph, selectedQuestion, result, updateStat
|
|||||||
{/* Current understanding */}
|
{/* Current understanding */}
|
||||||
{currentUnderstanding && (
|
{currentUnderstanding && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-1 text-[11px] font-medium tracking-widest uppercase text-gray-400/70">
|
<h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
What we understand so far
|
What we understand so far
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm leading-relaxed text-gray-600">{currentUnderstanding}</p>
|
<p className="text-sm leading-relaxed text-gray-600">{currentUnderstanding}</p>
|
||||||
|
|||||||
+1549
-150
File diff suppressed because it is too large
Load Diff
+231
-60
@@ -5,6 +5,8 @@ import { useState, useRef, useMemo } from "react";
|
|||||||
import DiagnosticsView from "@/components/diagnostics-view";
|
import DiagnosticsView from "@/components/diagnostics-view";
|
||||||
import ReasoningWorkspace, { LoadingOverlay, ContinueLaterBanner } from "@/components/reasoning-workspace";
|
import ReasoningWorkspace, { LoadingOverlay, ContinueLaterBanner } from "@/components/reasoning-workspace";
|
||||||
import { mockFetch, AVAILABLE_SCENARIOS } from "@/lib/mocks/confidence-engine/mock-client";
|
import { mockFetch, AVAILABLE_SCENARIOS } from "@/lib/mocks/confidence-engine/mock-client";
|
||||||
|
import { deriveFindingsFromContributions, normalizeFindings, produceFindingInformedSummary } from "@/lib/graph/finding-helpers";
|
||||||
|
import { loadInvestigation, saveInvestigation, clearInvestigation } from "@/lib/storage/investigation-storage";
|
||||||
|
|
||||||
/* Compile-time env resolution — NEXT_PUBLIC_ vars are injected by Next.js at build */
|
/* 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";
|
const MOCK_ENABLED = process.env.NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS === "true";
|
||||||
@@ -33,7 +35,7 @@ export async function submitScenarioForStartCase(fetchImpl, scenario) {
|
|||||||
|
|
||||||
export async function submitAnswerForUpdateCase(
|
export async function submitAnswerForUpdateCase(
|
||||||
fetchImpl,
|
fetchImpl,
|
||||||
{ situationGraph, previousQuestion, answer },
|
{ situationGraph, previousQuestion, answer, findings },
|
||||||
) {
|
) {
|
||||||
if (!answer?.trim()) {
|
if (!answer?.trim()) {
|
||||||
return {
|
return {
|
||||||
@@ -47,10 +49,15 @@ export async function submitAnswerForUpdateCase(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = { situationGraph, previousQuestion, answer };
|
||||||
|
if (findings && findings.length > 0) {
|
||||||
|
body.findings = findings;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetchImpl("/api/cases/update", {
|
const response = await fetchImpl("/api/cases/update", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ situationGraph, previousQuestion, answer }),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -186,25 +193,37 @@ export function UpdateErrorPanel({ updateError }) {
|
|||||||
|
|
||||||
export { INITIAL_MESSAGES, UPDATE_MESSAGES, useLoadingStatus };
|
export { INITIAL_MESSAGES, UPDATE_MESSAGES, useLoadingStatus };
|
||||||
|
|
||||||
// ── Session key ────────────────────────────────────────────────
|
|
||||||
const SESSION_KEY = "confidence-engine-session";
|
|
||||||
|
|
||||||
function getSession() {
|
/**
|
||||||
if (typeof sessionStorage === "undefined") return null;
|
* Derives whether the current component state represents a valid investigation
|
||||||
try {
|
* context sufficient to render a workspace surface.
|
||||||
const raw = sessionStorage.getItem(SESSION_KEY);
|
*
|
||||||
return raw ? JSON.parse(raw) : null;
|
* Valid only when:
|
||||||
} catch (_) { return null; }
|
* - result carries a situationGraph (renderable graph), OR
|
||||||
|
* - status is "success" AND there is a non-empty scenario
|
||||||
|
* (from session restoration with real data).
|
||||||
|
*
|
||||||
|
* This predicate is the single source of truth for all render-gate decisions.
|
||||||
|
* showExperimentView, fixture availability, or sessionStorage keys alone are
|
||||||
|
* NOT sufficient to constitute valid context.
|
||||||
|
*/
|
||||||
|
export function hasValidInvestigationContext(result, status, scenario) {
|
||||||
|
return Boolean(result?.situationGraph) ||
|
||||||
|
(status === "success" && Boolean(scenario?.trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSession(state) {
|
/**
|
||||||
if (typeof sessionStorage === "undefined") return;
|
* Derives the primary surface that must render for the given state tuple.
|
||||||
try { sessionStorage.setItem(SESSION_KEY, JSON.stringify(state)); } catch (_) {}
|
* Enforces exactly-one-primary-surface invariant: no zero, no two.
|
||||||
}
|
*/
|
||||||
|
export function derivePrimarySurface(result, status, _showExperimentView, scenario, activeBranchId) {
|
||||||
|
if (status === "loading") return "LOADING";
|
||||||
|
if (status === "error") return "ERROR_SURFACE";
|
||||||
|
|
||||||
function clearSession() {
|
const valid = hasValidInvestigationContext(result, status, scenario);
|
||||||
if (typeof sessionStorage === "undefined") return;
|
|
||||||
try { sessionStorage.removeItem(SESSION_KEY); } catch (_) {}
|
if (valid) return "NORMAL_WORKSPACE";
|
||||||
|
return "SCENARIO_ENTRY";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ScenarioForm() {
|
export default function ScenarioForm() {
|
||||||
@@ -219,19 +238,145 @@ export default function ScenarioForm() {
|
|||||||
const [currentUnderstanding, setCurrentUnderstanding] = useState(null);
|
const [currentUnderstanding, setCurrentUnderstanding] = useState(null);
|
||||||
const [mockScenario, setMockScenario] = useState("");
|
const [mockScenario, setMockScenario] = useState("");
|
||||||
const [hideFacilitatorOnLanding, setHideFacilitatorOnLanding] = useState(false);
|
const [hideFacilitatorOnLanding, setHideFacilitatorOnLanding] = useState(false);
|
||||||
|
|
||||||
|
/* ── RTO.31: focused contributions ownership ─────────────── */
|
||||||
|
const [focusedContributions, setFocusedContributions] = useState([]);
|
||||||
|
|
||||||
|
/* ── v2 findings from focused contributions ─────────────── */
|
||||||
|
const [findings, setFindings] = useState([]);
|
||||||
|
|
||||||
|
function appendFinding(finding) {
|
||||||
|
setFindings((prev) => {
|
||||||
|
return [...prev, finding];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFindingDisposition(findingId, newDisposition) {
|
||||||
|
setFindings((prev) =>
|
||||||
|
prev.map((f) => (f.id === findingId ? { ...f, userDisposition: newDisposition } : f)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFindingProposition(findingId, newProposition) {
|
||||||
|
setFindings((prev) =>
|
||||||
|
prev.map((f) =>
|
||||||
|
f.id === findingId
|
||||||
|
? { ...f, proposition: newProposition, userDisposition: null }
|
||||||
|
: f,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v0.49 promotion seam — deterministic Current Understanding update
|
||||||
|
* triggered by "Done for now" activity boundary (no case/update, no LLM).
|
||||||
|
*/
|
||||||
|
function handleDoneForNowPromotion(targetNodeId) {
|
||||||
|
if (!targetNodeId || !findings?.length) return;
|
||||||
|
|
||||||
|
// Filter eligible findings for this specific target only.
|
||||||
|
const eligible = findings.filter(
|
||||||
|
(f) => f.originatingTargetNodeId === targetNodeId && (f.userDisposition === null || f.userDisposition === "agree"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eligible.length === 0) return;
|
||||||
|
|
||||||
|
// Determine the base: use currentUnderstanding if available, else empty string.
|
||||||
|
const baseSummary = currentUnderstanding ?? "";
|
||||||
|
|
||||||
|
// Deterministic producer — no LLM, no API.
|
||||||
|
const newSummary = produceFindingInformedSummary(baseSummary, eligible);
|
||||||
|
|
||||||
|
// Idempotence guard: skip if summary is unchanged (no new eligible findings
|
||||||
|
// beyond what's already in the current Evidence block).
|
||||||
|
if (newSummary === baseSummary) return;
|
||||||
|
|
||||||
|
// Avoid duplicate evidence propositions from repeated promotion.
|
||||||
|
const existingEvidenceMatch = baseSummary.match(/Evidence:\s*\[([^\]]+)\]/);
|
||||||
|
let isDuplicate = false;
|
||||||
|
if (existingEvidenceMatch) {
|
||||||
|
const existingTexts = existingEvidenceMatch[1].split("; ").map((t) => t.trim());
|
||||||
|
isDuplicate = eligible.every((f) => existingTexts.includes(f.proposition));
|
||||||
|
}
|
||||||
|
if (isDuplicate) return;
|
||||||
|
|
||||||
|
// Mutate the SAME summary/state that autosave already persists.
|
||||||
|
setCurrentUnderstanding(newSummary);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendFocusedContribution(contribution) {
|
||||||
|
// Derive a single stored contribution object and use it for BOTH
|
||||||
|
// contribution storage AND Finding derivation so the same identity
|
||||||
|
// appears in focusedContributions[] and Finding.contributionId.
|
||||||
|
setFocusedContributions((prev) => {
|
||||||
|
const seq = prev.length + 1;
|
||||||
|
const storedContribution = { ...contribution, sequence: seq, id: `contrib-${String(seq).padStart(4, "0")}` };
|
||||||
|
|
||||||
|
// Derive Findings from the exact stored Contribution (not a separate approximation)
|
||||||
|
setFindings((prevFindings) => {
|
||||||
|
const newFindings = deriveFindingsFromContributions([storedContribution]).findings;
|
||||||
|
return normalizeFindings([...prevFindings, ...newFindings]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...prev, storedContribution];
|
||||||
|
});
|
||||||
|
}
|
||||||
const textareaRef = useRef(null);
|
const textareaRef = useRef(null);
|
||||||
|
|
||||||
/* Restore persisted session on mount (Phase 3) ─────────── */
|
/* ── Valid investigation predicate ─────────────────────── */
|
||||||
|
|
||||||
|
// Delegated to the exported utility below.
|
||||||
|
const validCtx = hasValidInvestigationContext(result, status, scenario);
|
||||||
|
|
||||||
|
/* Restore persisted session on mount ─────────── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
const saved = getSession();
|
const saved = loadInvestigation();
|
||||||
if (!saved) return;
|
if (!saved) return;
|
||||||
|
|
||||||
|
const hasGraph = Boolean(saved.situationGraph);
|
||||||
|
|
||||||
setScenario(saved.scenario || "");
|
setScenario(saved.scenario || "");
|
||||||
setResult(saved.situationGraph ? { ...saved, situationGraph: saved.situationGraph } : null);
|
setResult(hasGraph ? { ...saved, situationGraph: saved.situationGraph } : null);
|
||||||
setCurrentUnderstanding(saved.summary || null);
|
setCurrentUnderstanding(saved.summary || null);
|
||||||
setStatus("success");
|
setFocusedContributions(saved.focusedContributions || []);
|
||||||
|
setFindings(saved.findings || []);
|
||||||
|
|
||||||
|
// Partial sessions (present but no graph) must NOT suppress the
|
||||||
|
// scenario-entry form. Only promote to success when there is actual
|
||||||
|
// investigation data to render.
|
||||||
|
if (hasGraph) {
|
||||||
|
setStatus("success");
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/* ── Canonical autosave — persist whenever state changes (Phase 2) ── */
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
// Guard: no valid investigation yet → skip autosave during idle/start flows.
|
||||||
|
// Also prevents overwriting an existing saved investigation with the initial
|
||||||
|
// empty state of a fresh ScenarioForm instance (hydration race guard).
|
||||||
|
if (!result?.situationGraph) return;
|
||||||
|
|
||||||
|
void saveInvestigation({
|
||||||
|
scenario,
|
||||||
|
situationGraph: result.situationGraph,
|
||||||
|
selectedQuestion: result.selectedQuestion,
|
||||||
|
summary: currentUnderstanding,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
focusedContributions,
|
||||||
|
findings,
|
||||||
|
});
|
||||||
|
}, [
|
||||||
|
scenario,
|
||||||
|
result?.situationGraph,
|
||||||
|
result?.selectedQuestion,
|
||||||
|
currentUnderstanding,
|
||||||
|
focusedContributions,
|
||||||
|
findings,
|
||||||
|
]);
|
||||||
|
|
||||||
/* Restore facilitator dismiss preference (Experiment 05) ─── */
|
/* Restore facilitator dismiss preference (Experiment 05) ─── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
@@ -307,7 +452,7 @@ export default function ScenarioForm() {
|
|||||||
setCurrentUnderstanding(data.summary ?? null);
|
setCurrentUnderstanding(data.summary ?? null);
|
||||||
const normalised = normaliseStartResult(data);
|
const normalised = normaliseStartResult(data);
|
||||||
setResult(normalised);
|
setResult(normalised);
|
||||||
saveSession({ scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString() });
|
saveInvestigation({ scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString(), focusedContributions, findings: [] });
|
||||||
} else {
|
} else {
|
||||||
setStatus("error");
|
setStatus("error");
|
||||||
setCurrentUnderstanding(data.summary ?? null);
|
setCurrentUnderstanding(data.summary ?? null);
|
||||||
@@ -340,6 +485,7 @@ export default function ScenarioForm() {
|
|||||||
situationGraph: result?.situationGraph,
|
situationGraph: result?.situationGraph,
|
||||||
previousQuestion: result?.selectedQuestion,
|
previousQuestion: result?.selectedQuestion,
|
||||||
answer,
|
answer,
|
||||||
|
findings,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (submission.skipped) {
|
if (submission.skipped) {
|
||||||
@@ -352,6 +498,12 @@ export default function ScenarioForm() {
|
|||||||
const outcome = submission.data;
|
const outcome = submission.data;
|
||||||
|
|
||||||
if (submission.ok && outcome.success) {
|
if (submission.ok && outcome.success) {
|
||||||
|
// Merge server-returned findings with local state
|
||||||
|
let newFindings = [...findings];
|
||||||
|
if (outcome.appendedFindings && Array.isArray(outcome.appendedFindings)) {
|
||||||
|
newFindings = [...newFindings, ...outcome.appendedFindings];
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateStatus("success");
|
setUpdateStatus("success");
|
||||||
setCurrentUnderstanding(
|
setCurrentUnderstanding(
|
||||||
outcome.summary ? outcome.summary : currentUnderstanding,
|
outcome.summary ? outcome.summary : currentUnderstanding,
|
||||||
@@ -372,8 +524,8 @@ export default function ScenarioForm() {
|
|||||||
diagnostics: outcome.diagnostics,
|
diagnostics: outcome.diagnostics,
|
||||||
}));
|
}));
|
||||||
setAnswer("");
|
setAnswer("");
|
||||||
// Persist after successful update turn
|
// Persist after successful update turn — include findings
|
||||||
saveSession({ scenario, situationGraph: outcome.updatedSituationGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: outcome.summary ?? currentUnderstanding, updatedAt: new Date().toISOString() });
|
saveInvestigation({ scenario, situationGraph: outcome.updatedSituationGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: outcome.summary ?? currentUnderstanding, updatedAt: new Date().toISOString(), focusedContributions, findings: newFindings });
|
||||||
} else {
|
} else {
|
||||||
setUpdateStatus("error");
|
setUpdateStatus("error");
|
||||||
setUpdateError(outcome);
|
setUpdateError(outcome);
|
||||||
@@ -386,7 +538,8 @@ export default function ScenarioForm() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{status === "idle" && (
|
{/* ── Idle form for scenario input ─ */}
|
||||||
|
{!result?.situationGraph && status === "idle" && (
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
|
||||||
{/* Two-column landing workspace */}
|
{/* Two-column landing workspace */}
|
||||||
@@ -419,7 +572,7 @@ export default function ScenarioForm() {
|
|||||||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="dismiss-facilitator" className="text-xs text-gray-500">
|
<label htmlFor="dismiss-facilitator" className="text-xs text-gray-500">
|
||||||
Don't show this introduction again
|
{`Dismiss this introduction permanently`}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -428,7 +581,7 @@ export default function ScenarioForm() {
|
|||||||
|
|
||||||
{/* Right panel — Workspace (2/3 on desktop) */}
|
{/* Right panel — Workspace (2/3 on desktop) */}
|
||||||
<div className={hideFacilitatorOnLanding ? "md:col-span-3" : "md:col-span-2"}>
|
<div className={hideFacilitatorOnLanding ? "md:col-span-3" : "md:col-span-2"}>
|
||||||
<h2 className="mb-4 text-xs font-bold tracking-widest uppercase text-gray-400">Tell me what's happening</h2>
|
<h2 className="mb-4 text-xs font-bold tracking-widest uppercase text-gray-400">What's the situation</h2>
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
value={scenario}
|
value={scenario}
|
||||||
@@ -505,42 +658,58 @@ export default function ScenarioForm() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Main result workspace ─────────────────────── */}
|
{/* ── Main result workspace ─── */}
|
||||||
{((status === "success" || status === "error") && status !== "loading") && (
|
{(status === "success" || status === "error") && (
|
||||||
<ReasoningWorkspace
|
<>
|
||||||
scenario={scenario}
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
status={status}
|
{/* Workspace — uses result from Analyse or Update only */}
|
||||||
updateStatus={updateStatus}
|
<div className="lg:col-span-2">
|
||||||
currentUnderstanding={currentUnderstanding}
|
<ReasoningWorkspace
|
||||||
result={{
|
scenario={scenario}
|
||||||
...(result || {}),
|
status={status}
|
||||||
situationGraph: updateResult?.updatedSituationGraph ?? result?.situationGraph,
|
updateStatus={updateStatus}
|
||||||
selectedQuestion: updateResult?.selectedQuestion ?? result?.selectedQuestion,
|
currentUnderstanding={currentUnderstanding}
|
||||||
newlySurfacedNodeIds: result?.newlySurfacedNodeIds || [],
|
result={{
|
||||||
diagnostics: result?.diagnostics || null,
|
...(result || {}),
|
||||||
updateError,
|
situationGraph: updateResult?.updatedSituationGraph ?? result?.situationGraph,
|
||||||
}}
|
selectedQuestion: updateResult?.selectedQuestion ?? result?.selectedQuestion,
|
||||||
answer={answer}
|
newlySurfacedNodeIds: result?.newlySurfacedNodeIds || [],
|
||||||
setAnswer={setAnswer}
|
diagnostics: result?.diagnostics || null,
|
||||||
onAnswerSubmit={handleUpdate}
|
updateError,
|
||||||
lastSubmittedAnswer={lastSubmittedAnswer}
|
}}
|
||||||
onRestart={() => {
|
answer={answer}
|
||||||
clearSession();
|
setAnswer={setAnswer}
|
||||||
setStatus("idle");
|
onAnswerSubmit={handleUpdate}
|
||||||
setResult(null);
|
lastSubmittedAnswer={lastSubmittedAnswer}
|
||||||
setAnswer("");
|
focusedContributions={focusedContributions}
|
||||||
setUpdateStatus("idle");
|
onFocusedContribution={appendFocusedContribution}
|
||||||
setUpdateResult(null);
|
findings={findings}
|
||||||
setLastSubmittedAnswer("");
|
onUpdateFindingDisposition={updateFindingDisposition}
|
||||||
setCurrentUnderstanding(null);
|
onUpdateFindingProposition={updateFindingProposition}
|
||||||
setUpdateError(null);
|
/* ── v0.49 — done-for-now promotion seam ─────────── */
|
||||||
}}
|
onSummaryUpdate={handleDoneForNowPromotion}
|
||||||
/>
|
onRestart={() => {
|
||||||
|
clearInvestigation();
|
||||||
|
setStatus("idle");
|
||||||
|
setResult(null);
|
||||||
|
setAnswer("");
|
||||||
|
setUpdateStatus("idle");
|
||||||
|
setUpdateResult(null);
|
||||||
|
setLastSubmittedAnswer("");
|
||||||
|
setCurrentUnderstanding(null);
|
||||||
|
setUpdateError(null);
|
||||||
|
setFocusedContributions([]);
|
||||||
|
setFindings([]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Continue later banner when session was restored ── */}
|
{/* ── Continue later banner when session was restored ── */}
|
||||||
{status === "success" && result?.updatedAt && (
|
{status === "success" && result?.updatedAt && (
|
||||||
<ContinueLaterBanner onRestart={() => { clearSession(); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); }} />
|
<ContinueLaterBanner onRestart={() => { clearInvestigation(); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); setFocusedContributions([]); setFindings([]); }} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Reset button after successful analysis */}
|
{/* Reset button after successful analysis */}
|
||||||
@@ -548,7 +717,7 @@ export default function ScenarioForm() {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
clearSession();
|
clearInvestigation();
|
||||||
setScenario("");
|
setScenario("");
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
setResult(null);
|
setResult(null);
|
||||||
@@ -558,6 +727,8 @@ export default function ScenarioForm() {
|
|||||||
setLastSubmittedAnswer("");
|
setLastSubmittedAnswer("");
|
||||||
setCurrentUnderstanding(null);
|
setCurrentUnderstanding(null);
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
setFocusedContributions([]);
|
||||||
|
setFindings([]);
|
||||||
}}
|
}}
|
||||||
className="rounded-lg border border-gray-200/60 px-4 py-2 text-sm font-medium text-gray-500 transition hover:bg-gray-50/80"
|
className="rounded-lg border border-gray-200/60 px-4 py-2 text-sm font-medium text-gray-500 transition hover:bg-gray-50/80"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -211,6 +211,160 @@ Given the useful investigation structure the Engine can already derive, how shou
|
|||||||
|
|
||||||
The next phase should begin from this methodology question, not from a preselected technical solution.
|
The next phase should begin from this methodology question, not from a preselected technical solution.
|
||||||
|
|
||||||
|
## Granular Answer-Fragment Learning (RTO.14–17)
|
||||||
|
|
||||||
|
Recent experiments explored what happens when further answers are made inside the same focused investigation (RTO.14–17).
|
||||||
|
|
||||||
|
### What RTO.14–17 proved
|
||||||
|
|
||||||
|
The experiments demonstrated that an LLM can:
|
||||||
|
|
||||||
|
- Retain prior focused knowledge across turns
|
||||||
|
- Revise uncertainty in response to new information
|
||||||
|
- Separate focused understanding from decision significance
|
||||||
|
- Carry coherent reasoning across several turns inside a single investigation
|
||||||
|
|
||||||
|
This learning was valuable and should be preserved as experimental evidence. The apparatus created during RTO.14–17 remains available and relevant.
|
||||||
|
|
||||||
|
### What RTO.14–17 began recreating
|
||||||
|
|
||||||
|
Pushing that design further exposed that we had reproduced the original structural assumption at a lower level:
|
||||||
|
|
||||||
|
- **Original global pattern:**
|
||||||
|
```text
|
||||||
|
whole case state + new answer → LLM rewrites whole case state
|
||||||
|
```
|
||||||
|
- **Focused version (RTO.14–17):**
|
||||||
|
```text
|
||||||
|
whole focused-investigation state + new answer → LLM rewrites whole focused-investigation state
|
||||||
|
```
|
||||||
|
|
||||||
|
The second version is much smaller and technically better, but it is still the same cumulative reconstruction pattern — just at a lower scope. Prompt growth from later RTO experiments helped expose this.
|
||||||
|
|
||||||
|
**Learning:** Do not immediately respond by optimising or compressing the cumulative focused-state implementation. Reconsider whether accumulated state needs to be sent back through the LLM at all.
|
||||||
|
|
||||||
|
### The granular answer-fragment hypothesis (working hypothesis — not yet architecture)
|
||||||
|
|
||||||
|
The natural reasoning unit appears to be:
|
||||||
|
|
||||||
|
> **one question → one answer → one interpretation/capture**
|
||||||
|
|
||||||
|
Granularity's purpose is not merely token or latency optimisation. The small cycle is how the methodology makes a large problem manageable for the user. A difficult scenario is progressively decomposed into pieces small enough to reason about confidently.
|
||||||
|
|
||||||
|
The working hypothesis is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user chooses a question
|
||||||
|
→ user provides an answer
|
||||||
|
→ Engine deconstructs that answer
|
||||||
|
→ Engine captures the granular contribution
|
||||||
|
→ resulting uncertainties/questions are exposed
|
||||||
|
→ user chooses what to investigate next
|
||||||
|
→ repeat
|
||||||
|
```
|
||||||
|
|
||||||
|
Each accepted answer can produce a small evidence-bearing reasoning fragment. Those fragments are remembered outside the LLM call. The larger investigation understanding and eventual graph emerge from composing those pieces over time. Only directly relevant prior knowledge may need to be supplied when a specific earlier fragment is being qualified, contradicted or refined.
|
||||||
|
|
||||||
|
A software implementation may eventually represent granular contributions as things such as:
|
||||||
|
|
||||||
|
- observations
|
||||||
|
- uncertainties
|
||||||
|
- assumptions
|
||||||
|
- relationships
|
||||||
|
- questions raised
|
||||||
|
|
||||||
|
linked to the question/investigation that produced them. This illustrative list is not a production schema — it exists here only as a design hint.
|
||||||
|
|
||||||
|
### Memory / graph principle
|
||||||
|
|
||||||
|
The LLM does not necessarily need to own accumulated reasoning memory. The graph/state/notebook layer can remember the reasoning fragments. The LLM may be used to interpret a new answer, but a software implementation should not assume every new answer requires sending all accumulated investigation state back through the model and asking it to regenerate the whole current understanding.
|
||||||
|
|
||||||
|
### Optional capability: "Help me answer" / "Answer for me"
|
||||||
|
|
||||||
|
A software implementation may optionally offer something like:
|
||||||
|
|
||||||
|
> **Help me answer** or **Answer for me**
|
||||||
|
|
||||||
|
where the LLM proposes an answer. This is an optional application capability — not part of the core method. The methodology works without it.
|
||||||
|
|
||||||
|
**Ownership rule:** A generated answer is a proposal, not gospel and not automatically evidence. The user must be able to accept it, edit it or reject it. Only an accepted contribution enters the normal reasoning/deconstruction flow. Where practical, provenance should remain distinguishable between:
|
||||||
|
|
||||||
|
- user-supplied answer
|
||||||
|
- LLM-proposed answer accepted/edited by user
|
||||||
|
|
||||||
|
### Development principle reaffirmed: BUILD → BREAK → LEARN → STOP
|
||||||
|
|
||||||
|
When an experiment exposes that an architectural assumption is breaking:
|
||||||
|
|
||||||
|
```text
|
||||||
|
do not immediately optimise the broken assumption
|
||||||
|
do not add complexity to preserve it
|
||||||
|
capture what was learned
|
||||||
|
return to the methodology
|
||||||
|
design the next smallest experiment from that learning
|
||||||
|
```
|
||||||
|
|
||||||
|
RTO.14–17 should therefore remain valuable evidence, not be deleted or described as mistakes. They helped reveal the next underlying assumption.
|
||||||
|
|
||||||
|
## Methodology test for future development
|
||||||
|
|
||||||
|
> **Could this reasoning operation be described in the Confidence Engine methodology and performed by a trained human facilitator without an LLM?**
|
||||||
|
|
||||||
|
- If YES: the application may use an LLM to automate, accelerate or scale it
|
||||||
|
- If NO: stop and ask whether the work is developing the Confidence Engine methodology or merely exploiting an LLM capability
|
||||||
|
|
||||||
|
This does not apply to implementation mechanics such as JSON, APIs or databases. It applies to the underlying reasoning behaviour.
|
||||||
|
|
||||||
|
## Recent experimental evidence supporting methodological principles (2026-08-18/19)
|
||||||
|
|
||||||
|
The following experiments provide specific evidence for the durable methodology principles
|
||||||
|
documented in `docs/current-working-principles.md`. Each is recorded as one data point, not generalisation.
|
||||||
|
|
||||||
|
### RTO.18 — Independent granular question/answer deconstruction (without accumulated state)
|
||||||
|
|
||||||
|
Independent per-turn question and answer deconstruction worked when each turn received only its own
|
||||||
|
question + answer, without any accumulated focused state from previous turns. This supports:
|
||||||
|
|
||||||
|
- **A3** (reasoning on meaning, not accumulated vocabulary)
|
||||||
|
- **A6** (non-linear investigation via independent fragments)
|
||||||
|
- **The granular answer-fragment hypothesis** as a working direction
|
||||||
|
|
||||||
|
### RTO.20 — Narrow derived current view from selected fragments + known relationship
|
||||||
|
|
||||||
|
A narrow, derived current understanding state worked when computed from selected fragments combined with known structural relationships rather than full-graph reconstruction. This supports:
|
||||||
|
|
||||||
|
- **A5** (deterministic structure for identity/storage; semantic interpretation only where needed)
|
||||||
|
- **A10** (progressive disclosure of relevant reasoning to the user)
|
||||||
|
|
||||||
|
### RTO.21 — Semantic relationship discovery: one genuine positive case
|
||||||
|
|
||||||
|
Semantic interpretation found one genuine cross-fragment relationship from two fragments alone. The operation correctly identified that two contributions meaningfully related without prior keyword dictionary matching. This supports:
|
||||||
|
|
||||||
|
- **A4** (semantic interpretation as a suitable facilitation capability)
|
||||||
|
- **A3** (meaning-based over vocabulary-based reasoning)
|
||||||
|
|
||||||
|
### RTO.22 — Semantic relationship discovery: one obvious negative case (control)
|
||||||
|
|
||||||
|
The same semantic operation correctly returned no relationship for one obviously unrelated pair of contributions. This supports:
|
||||||
|
|
||||||
|
- **A4** (semantic interpretation is useful but produces proposals, not decisions)
|
||||||
|
- **A3** (meaning-based reasoning does not produce false positives at high rates on obvious cases)
|
||||||
|
|
||||||
|
### RTO.23 — Current apparatus work
|
||||||
|
|
||||||
|
RTO.23 apparatus development is ongoing. No live experimental evidence exists for RTO.23 yet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Methodology principles reinforced by this evidence
|
||||||
|
|
||||||
|
The experiments above support (without proving) the following durable methodology boundaries:
|
||||||
|
|
||||||
|
- **Delivery-platform independence** (A1): all results were observed through a software delivery path, but the reasoning operations described (question deconstruction, relationship inference, fragment composition) are equally performable by a human facilitator.
|
||||||
|
- **Meaning over dictionary** (A3/A4): RTO.21 and RTO.22 together suggest semantic interpretation can produce both true-positive and true-negative relationship proposals without keyword scoring — but two data points do not establish reliability. The guardrail remains: treat all inferred relationships as proposals until handled per the delivery method.
|
||||||
|
- **Non-linear investigation** (A6/A7): independent fragment processing validates that reasoning can proceed asynchronously across branches without blocking the user.
|
||||||
|
- **Progressive disclosure** (A10): RTO.20 demonstrates that a derived narrow view from relevant fragments is more useful to the user than a full-graph reconstruction of everything known.
|
||||||
|
|
||||||
## Source basis
|
## Source basis
|
||||||
|
|
||||||
- `01_Confidence_Engine_Founding_Principles`
|
- `01_Confidence_Engine_Founding_Principles`
|
||||||
@@ -224,4 +378,11 @@ The next phase should begin from this methodology question, not from a preselect
|
|||||||
- `Confidence_Engine_Project_Context_Update_2026-08-17`
|
- `Confidence_Engine_Project_Context_Update_2026-08-17`
|
||||||
- `Confidence_Engine_Current_Handoff_2026-08-17`
|
- `Confidence_Engine_Current_Handoff_2026-08-17`
|
||||||
|
|
||||||
This context update distinguishes established project principles from current implementation learning. The workspace/user-directed investigation model is recorded as the current hypothesis to test, not as a completed replacement architecture.
|
> **Provenance note:** Some source-basis documents listed above were external
|
||||||
|
> project/session context supplied during the methodology work and are not
|
||||||
|
> repository-managed files. They informed this document's content but cannot be
|
||||||
|
> verified as originating from the Git history of this repository. Their role
|
||||||
|
> is to document where the methodology context came from, not to assert Git
|
||||||
|
> provenance for those external documents.
|
||||||
|
|
||||||
|
This context update distinguishes established project principles from current implementation learning. The workspace/user-directed investigation model is recorded as the current hypothesis to test, not as a completed replacement architecture. The granular answer-fragment hypothesis (RTO.14–17) is recorded as working hypothesis, not yet accepted architecture.
|
||||||
@@ -15,6 +15,30 @@ All files below were moved from `docs/` on 2026-08-06 by Experiment 29 to reduce
|
|||||||
| `docs/v0.7-observation-report.md` (136 lines) | `docs/archive/v0.7-observation-report.md` | Experimental observation snapshot from v0.7 UX work. | Useful as a reference but not a current working document. UX work is paused. | When reviewing past UX observations that may inform future interface design decisions. |
|
| `docs/v0.7-observation-report.md` (136 lines) | `docs/archive/v0.7-observation-report.md` | Experimental observation snapshot from v0.7 UX work. | Useful as a reference but not a current working document. UX work is paused. | When reviewing past UX observations that may inform future interface design decisions. |
|
||||||
| `docs/archive/deferred-ux-backlog.md` (376 lines) | `docs/archive/deferred-ux-backlog.md` | Deferred and exploratory UX ideas from original `docs/backlog info.md` (lines 21–390). Retained for historical reference. Not commitments, priorities or active tasks. | Superseded `docs/backlog info.md`. Deferred UX planning separated from mock reference in Experiment 31. | When a named past UX idea from the deferred backlog is being reviewed; not loaded by default. |
|
| `docs/archive/deferred-ux-backlog.md` (376 lines) | `docs/archive/deferred-ux-backlog.md` | Deferred and exploratory UX ideas from original `docs/backlog info.md` (lines 21–390). Retained for historical reference. Not commitments, priorities or active tasks. | Superseded `docs/backlog info.md`. Deferred UX planning separated from mock reference in Experiment 31. | When a named past UX idea from the deferred backlog is being reviewed; not loaded by default. |
|
||||||
|
|
||||||
|
## Phase 2B Experiment Archives (2026-08-19)
|
||||||
|
|
||||||
|
All files below were classified `HISTORICAL_EVIDENCE + SAFE` during the Phase 1B/2B context audit and moved to reduce default reading burden while preserving full traceability. They are preserved evidence — not discarded, obsolete, or invalidated. Load only when a specific historical question requires them.
|
||||||
|
|
||||||
|
| Subdirectory | What Was Moved | Count |
|
||||||
|
|---|---|---|
|
||||||
|
| `docs/archive/experiments/reasoning-fidelity-v0.8/` | Experiment 56 family (reasoning-fidelity v0.8 pass) | 11 files (experiment-56a–m, excluding c) |
|
||||||
|
| `docs/archive/experiments/semantic-action-contract/` | Experiment 58 family (semantic action contract) | 8 files (experiment-58a1–a6, b1–b2) |
|
||||||
|
| `docs/archive/experiments/question-formulation/` | Experiment 59 family (question formulation) | 7 files (experiment-59a1–a3, b1–b4) |
|
||||||
|
| `docs/archive/experiments/decision-options/` | Experiment 60A family (decision options analysis) | 7 files (experiment-60a1–8, excluding a3) |
|
||||||
|
| `docs/archive/experiments/decision-closure-integration/` | Experiment 60B subfamilies {10–15}, {55–82}, {95,97,100} | 35 files (experiment-60b{10-15}, {55-56,58-82}, {95,97,100}) |
|
||||||
|
| `docs/archive/experiments/knowledge-mgmt/` | Cold-start validation historical evidence | 1 file (cold-start-validation.md) |
|
||||||
|
| `docs/archive/experiments/context-routing/` | Document-role review (classification/routing analysis) | 1 file (document-role-review.md) |
|
||||||
|
| `docs/archive/experiments/pre-RTO/` | Pre-Return-to-Origin experiments and version-specific docs: v0.5–v0.7 | 7 files (pre-RTO experiments + release notes/UX pass) |
|
||||||
|
|
||||||
|
**Not moved in Phase 2B:** checkpoint-60b93.md, docs/design-evolution-log.md, docs/investigation-state-assessment*.md, architectural-principles.md, v0.6-reasoning-architecture.md, success-signals.md, failure-modes.md, investigation-narrative.md, behaviour-selection.md, orchestrator-contract.md, reasoning-contract-backlog.md, reasoning-refinement-requirements.md, reasoning-production-path-map.md.
|
||||||
|
|
||||||
|
**Phase 2D experiment archives (2026-08-19):** After Phase 2C carry-forward verification confirmed all three families SAFE for archival:
|
||||||
|
|
||||||
|
| Subdirectory | What Was Moved | Count |
|
||||||
|
|---|---|---|
|
||||||
|
| `docs/archive/experiments/post-v0.8-investigation/` | Experiment 57 family (post-v0.8 investigation) | 69 files (experiment-57* family) |
|
||||||
|
| `docs/archive/experiments/decision-closure-integration/` | Experiment 60B subfamilies {1–8}, {19–48} | 37 files (experiment-60b{1-8}, experiment-60b{19-48}) |
|
||||||
|
|
||||||
## Superseded Files
|
## Superseded Files
|
||||||
|
|
||||||
The following files were superseded by a structured split in Experiment 31 and are no longer in use. Their contents remain fully represented in the documents below.
|
The following files were superseded by a structured split in Experiment 31 and are no longer in use. Their contents remain fully represented in the documents below.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user