Files
learnharness/tests/graph-view-filters.test.ts
T
amosblomqvist 2cba283009 Graph cost data drops the dead cluster-naming channel from the UI
The cluster namer is dead (owner refinement round, 2026-09-24): cluster
labels are just session titles / file names now, coined without an LLM,
so there is no reason to keep tracking naming spend in the cost UI.

components/graph-view.tsx (UI-only; the core fold is untouched):
- NEW pure export displayCostTotals(costs) = extraction + audit dollars
  and unpriced counts. Both the cost pill and the panel headline read
  THIS — never GraphCostSummary.totalDollars, which still includes the
  historical naming channel — so the two can never disagree.
- GraphCostPanel's stage rows lose the "Cluster naming" line; the
  headline and unpriced counts come from displayCostTotals.
- The recent-runs list filters out kind === "naming" runs entirely
  (the "N labels" tail died with them).
- Mount comment updated: the fold covers chat + notes extraction runs.

Note for master/Platform: src/core/cost-summary.ts still folds
naming-runs.jsonl into stages.naming / totalDollars (Platform's lane).
The UI no longer reads those fields; Platform can cull the seam and the
dead ledger whenever convenient — displayCostTotals already isolates
the display from whatever the core returns.

Pinned in tests/graph-view-filters.test.ts: displayCostTotals with a
naming stage of $9.99 + 2 unpriced proves the display sees extraction
+ audit only ($2.00, 1 unpriced). Live-verified on :3001 — the panel
renders Extraction and Audit rows, no naming row, no naming runs in
the recent list. 905/905.
2026-09-24 19:25:37 +03:00

70 lines
2.0 KiB
TypeScript

/**
* Legend/filters predicate tests — the pure half of the graph-view filters
* panel (isNodeVisible); the sigma reducers just consume its verdict.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { displayCostTotals, isNodeVisible } from "../components/graph-view.tsx";
const node = {
name: "Money multiplier",
statement: "The maximum ratio of broad money a bank can create from each unit of base money.",
};
describe("isNodeVisible (search predicate)", () => {
it("is visible with an empty query", () => {
assert.equal(isNodeVisible(node, ""), true);
});
it("search matches the statement case-insensitively", () => {
assert.equal(isNodeVisible(node, "MONEY multiplier"), true);
});
it("search matches the statement when the name does not", () => {
assert.equal(isNodeVisible(node, "MAXIMUM ratio"), true);
assert.equal(isNodeVisible(node, " velocity"), false);
});
it("search matches the name when the statement does not", () => {
assert.equal(isNodeVisible(node, "money MULTIPLIER"), true);
assert.equal(isNodeVisible(node, " banking"), false);
});
it("whitespace-only query is no filter", () => {
assert.equal(isNodeVisible(node, " "), true);
});
});
describe("displayCostTotals (naming-free cost fold)", () => {
const stage = (dollars: number, unpricedRuns = 0) => ({
runs: dollars > 0 ? 1 : 0,
dollars,
unpricedRuns,
tokens: {
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
reasoningTokens: 0,
},
});
const costs = {
stages: {
extraction: stage(1.25, 1),
audit: stage(0.75),
naming: stage(9.99, 2), // dead channel — must never reach the display
},
} as const;
it("sums extraction + audit only — the dead namer's spend is invisible", () => {
assert.equal(displayCostTotals(costs).totalDollars, 2);
});
it("unpriced counts fold the same two stages", () => {
assert.equal(displayCostTotals(costs).unpricedRuns, 1);
});
});