diff --git a/components/reasoning-workspace.jsx b/components/reasoning-workspace.jsx
index 429459d..4cac47c 100644
--- a/components/reasoning-workspace.jsx
+++ b/components/reasoning-workspace.jsx
@@ -581,6 +581,10 @@ function ThreadContributionsBadge({ nodeId, contributions }) {
return (
+ {/* Thread activity cue: visible when this node has focused investigation history */}
+
+ INVESTIGATING
+
{/* Thread learning indicator — collapsed by default; user can expand to inspect history */}
diff --git a/tests/open-questions-vs-assumptions.test.jsx b/tests/open-questions-vs-assumptions.test.jsx
index 3f14275..0bf92aa 100644
--- a/tests/open-questions-vs-assumptions.test.jsx
+++ b/tests/open-questions-vs-assumptions.test.jsx
@@ -744,3 +744,116 @@ describe("same-node focused result reopen", () => {
expect(hasCompletedInvestigation("u5")).toBe(true);
});
});
+
+// ── INVESTIGATING cue on Open Question cards ───
+
+describe("Focused investigation history cue", () => {
+ function getThreadContribs(nodeId, contributions) {
+ return (contributions || []).filter((c) => c.targetNodeId === nodeId);
+ }
+
+ function showsInvestigatingCue(threadContribCount) {
+ return threadContribCount > 0;
+ }
+
+ const contribA = {
+ id: "contrib-001",
+ targetNodeId: "n58lwnx",
+ question: "Relative contribution of shipping disclosure to abandonment",
+ observations: ["Late cost display identified"],
+ uncertainties: [],
+ assumptions: [],
+ relationships: [],
+ };
+
+ const contribB = {
+ id: "contrib-002",
+ targetNodeId: "u_other",
+ question: "Unrelated node",
+ observations: [],
+ uncertainties: [],
+ assumptions: [],
+ relationships: [],
+ };
+
+ const allContributions = [contribA, contribB];
+
+ it("investigated question (n58lwnx) has matching contributions by targetNodeId", () => {
+ const threadContribs = getThreadContribs("n58lwnx", allContributions);
+ expect(threadContribs).toHaveLength(1);
+ expect(threadContribs[0].id).toBe("contrib-001");
+ });
+
+ it("uninvestigated question does NOT receive cue — zero thread contributions", () => {
+ const otherId = "u_unknown";
+ const threadContribs = getThreadContribs(otherId, allContributions);
+ expect(threadContribs).toHaveLength(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(false);
+ });
+
+ it("unrelated contributions do NOT mark another question", () => {
+ const otherNode = "u_somethingElse";
+ const threadContribs = getThreadContribs(otherNode, allContributions);
+ expect(threadContribs).toHaveLength(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(false);
+ });
+
+ it("investigated question DOES show cue (has 1+ thread contributions)", () => {
+ const threadContribs = getThreadContribs("n58lwnx", allContributions);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(true);
+ });
+
+ it("UNCLEAR and INVESTIGATING are independent — both can coexist on same node", () => {
+ const uNode = { id: "n58lwnx", kind: "unknown", status: "unclear", label: "U1" };
+ const hasContribs = getThreadContribs(uNode.id, allContributions).length > 0;
+ expect(uNode.status).toBe("unclear");
+ expect(hasContribs).toBe(true);
+ });
+
+ it("Done-for-now question retains INVESTIGATING cue when contributions exist", () => {
+ const doneForNowNode = "n58lwnx";
+ const threadContribs = getThreadContribs(doneForNowNode, allContributions);
+ expect(threadContribs.length).toBeGreaterThan(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(true);
+ });
+
+ it("identity rule distinguishes contributed node from uninvestigated", () => {
+ const invested = getThreadContribs("n58lwnx", allContributions);
+ const uninvested = getThreadContribs("u_unrelated", allContributions);
+ expect(invested.length).toBeGreaterThan(0);
+ expect(uninvested.length).toBe(0);
+ expect(showsInvestigatingCue(invested.length)).not.toBe(showsInvestigatingCue(uninvested.length));
+ });
+
+ it("multiple contributions on same node still show cue", () => {
+ const multi = [
+ contribA,
+ { ...contribA, id: "contrib-003", targetNodeId: "n58lwnx" },
+ { ...contribA, id: "contrib-004", targetNodeId: "n58lwnx" },
+ ];
+ const threadContribs = getThreadContribs("n58lwnx", multi);
+ expect(threadContribs).toHaveLength(3);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(true);
+ });
+
+ it("null contributions produce no cue", () => {
+ const threadContribs = getThreadContribs("n58lwnx", null);
+ expect(threadContribs).toHaveLength(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(false);
+ });
+
+ it("empty contributions produce no cue", () => {
+ const threadContribs = getThreadContribs("n58lwnx", []);
+ expect(threadContribs).toHaveLength(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(false);
+ });
+
+ it("contributions with mismatched targetNodeId do not match", () => {
+ const wrong = [
+ { id: "contrib-wrong", targetNodeId: "wrong-id" },
+ ];
+ const threadContribs = getThreadContribs("n58lwnx", wrong);
+ expect(threadContribs).toHaveLength(0);
+ expect(showsInvestigatingCue(threadContribs.length)).toBe(false);
+ });
+});