ui: round 8 — fuzzy session search, stacked doors, All right, teach divider at the step seam
- SESSION SEARCH goes fuzzy on the SAME fuzzyScore the model pickers use (de0c286's util — one scoring across every search surface): ranked by score instead of raw substring; empty query keeps order. - SIDEBAR: New Teaching / New Practice STACK (full-width rows); the kind filter reads Teaching · Practice · All — the real modes first, All as the escape hatch, mirroring the stack above. Home stays in the LH band (owner-confirmed). - TEACH DIVIDER, the stacked-dividers bug: the plan presentation and the first tagged quiz call usually share ONE assistant message, so the message-level seam drew Planning+Teaching back-to-back. New pure teachStepIndex(parts) finds the STEP boundary governing the flip call; the divider renders there (replacing that boundary's plain hairline — a labeled rule supersedes it), prepending before the whole message only when the flip is in its first step. The fold (chip, sidebar state) is untouched. - JOBS SUITE, flake round 3: the sweep scans the SHARED jobs dir, so parallel test FILES' running records respawn into this file's counters — the assertions go jurisdictional (own-job ids collected; foreign respawns are not this test's business). 1331/1331.
This commit is contained in:
@@ -13,6 +13,7 @@ import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { PhaseChip, PhaseDivider, planNodeCount } from "../components/phase-divider.tsx";
|
||||
import { teachStepIndex } from "../src/core/phases.ts";
|
||||
|
||||
describe("PhaseChip (header)", () => {
|
||||
it("renders the learner-facing label — no colored dot (phase color retired)", () => {
|
||||
@@ -79,6 +80,53 @@ describe("PhaseDivider (transcript boundary)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("teachStepIndex (WHERE teaching begins inside the flipping message — owner round 8)", () => {
|
||||
const call = { type: "tool-quiz", input: { nodeId: "n1", question: "q?" } };
|
||||
|
||||
it("the flip call in the message's FIRST step → -1 (divider prepends before the message)", () => {
|
||||
assert.equal(teachStepIndex([{ type: "step-start" }, { type: "text" }, call]), -1);
|
||||
});
|
||||
|
||||
it("a later step: the boundary's step-start index — between plan presentation and teaching", () => {
|
||||
// The stacked-dividers bug shape: plan_init + plan text, then a NEW
|
||||
// step that poses the first tagged quiz. The seam is the SECOND
|
||||
// step-start (index 3), not before the message.
|
||||
const parts = [
|
||||
{ type: "step-start" },
|
||||
{ type: "tool-plan_init", state: "output-available" },
|
||||
{ type: "text", text: "Here is your plan." },
|
||||
{ type: "step-start" },
|
||||
{ type: "text", text: "Let's begin." },
|
||||
call,
|
||||
];
|
||||
assert.equal(teachStepIndex(parts), 3);
|
||||
});
|
||||
|
||||
it("no step-start parts at all (defensive): first step → -1", () => {
|
||||
assert.equal(teachStepIndex([{ type: "text" }, call]), -1);
|
||||
});
|
||||
|
||||
it("an untagged quiz call is not the flip — scan continues to the tagged one", () => {
|
||||
const parts = [
|
||||
{ type: "step-start" },
|
||||
{ type: "tool-quiz", input: { question: "probe quiz — no nodeId" } },
|
||||
{ type: "step-start" },
|
||||
call,
|
||||
];
|
||||
assert.equal(teachStepIndex(parts), 2);
|
||||
});
|
||||
|
||||
it("explain calls carry nodeId too — same seam", () => {
|
||||
const parts = [
|
||||
{ type: "step-start" },
|
||||
{ type: "text" },
|
||||
{ type: "step-start" },
|
||||
{ type: "tool-explain", input: { nodeId: "n2", topic: "t" } },
|
||||
];
|
||||
assert.equal(teachStepIndex(parts), 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("planNodeCount (the seam message's tree)", () => {
|
||||
it("counts the first output-available plan tree in the message", () => {
|
||||
const count = planNodeCount({
|
||||
|
||||
Reference in New Issue
Block a user