Detail card: per-session understanding notes next to the session list

The extractor's per-proposition understanding observations (named-word
scale: solid/shaky/accepted/...) were ledger data since the redesign,
with no surface. This exposes them at read time and shows each
session's note as a small chip on its session row in the node detail
card — the per-session confidence history the user asked for, one
observation per (node, chat), latest wins within a chat.

- GraphSnapshot.nodeObservations: nodeId -> chatId -> note, built in
  graphSnapshot() from observation events (pure read-time join).
- Sessions list rows: link + observation chip.
- Session-relative judgments stay session-scoped: they render next to
  their session, never as node state (the state model stays pending).
This commit is contained in:
amosblomqvist
2026-09-09 01:25:14 +03:00
parent 54f88f37e1
commit 0fccbbe2ad
4 changed files with 54 additions and 11 deletions
+7 -1
View File
@@ -363,7 +363,7 @@ describe("the merger (mergeFindings)", () => {
assert.deepEqual(m, { chatId: chat2.id, sourceId: "q1", graphNodeId: alphaId });
});
it("derives node states and sessions in graphSnapshot", () => {
it("derives sessions and per-session understanding observations in graphSnapshot", () => {
const snap = graphSnapshot(graphDeps);
const idByStatement = new Map(snap.nodes.map((n) => [n.statement, n.id]));
// No node-state classes anymore (2026-09-08): the snapshot carries
@@ -378,6 +378,12 @@ describe("the merger (mergeFindings)", () => {
);
// Gamma was only ever touched by chat1.
assert.deepEqual(snap.nodeSessions[idByStatement.get("Gamma application")!], [chat1.id]);
// Understanding observations: (node → chat → the extractor's note).
// Alpha carries chat1's "solid after the check"; chat2's q1 has no note.
const alphaObs = snap.nodeObservations[idByStatement.get("Alpha intuition")!] ?? {};
assert.equal(alphaObs[chat1.id], "solid after the check");
assert.equal(alphaObs[chat2Id], undefined);
});
it("never string-matches without a verdict — an identical statement without linkTo is a NEW node", () => {