/** * SESSION-KIND PINS (practice sessions, owner 2026-10-04): ChatRecord.kind * is ABSENT-BY-DEFAULT — absence reads as teach, so pre-practice records * never migrate; "practice" marks an adaptive-practice session (the * practice agent + its two tools from turn 1, no extraction, no graph * writes). The store normalizes at the summary projection; createChat * stamps the marker only for practice. Runs under an isolated LH_DATA_DIR * (the chat-birth-stamp discipline: the seam is set before any * config-loading import, so every app-module import is dynamic). */ import assert from "node:assert/strict"; import fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { after, describe, it } from "node:test"; // ── the seam, BEFORE any config-loading import ────────────────────────────── const TMP_DATA = fs.mkdtempSync(path.join(os.tmpdir(), "lh-chat-kind-")); process.env.LH_DATA_DIR = TMP_DATA; const { chatStore } = await import("../src/store/chats.ts"); after(() => { fs.rmSync(TMP_DATA, { recursive: true, force: true }); }); describe("session kind (practice sessions, 2026-10-04)", () => { it("createChat default carries NO kind field — absence reads as teach", () => { const chat = chatStore.createChat("plain session"); assert.equal("kind" in chat, false, "teach records carry no kind marker"); }); it("createChat(kind: 'practice') stamps the marker on record + list", () => { const chat = chatStore.createChat("practice session", "practice"); assert.equal(chat.kind, "practice"); const listed = chatStore.listChats().find((c) => c.id === chat.id); assert.equal(listed?.kind, "practice"); }); it("the summary projection NORMALIZES: absent record field reads as teach", () => { const chat = chatStore.createChat("another plain one"); const listed = chatStore.listChats().find((c) => c.id === chat.id); assert.equal(listed?.kind, "teach"); }); });