Per-batch pipeline is now extract → spine → audit → merge (owner order, 2026-09-30), the spine inside the serialized audit section so it reads the graph exactly as the following audit will. Measured motive: 0 of 133 chains in the live corpus crossed a stretch boundary — no stage minted cross-stretch chains. - src/agent/subagents/spine.ts: SESSION/NOTES spine preambles + shared SPINE_CORE (span doctrine; extraction doctrines carried over), the submit_spine TypeBox schema, validateSpine (mechanical span law: every chain touches >=1 fresh endpoint AND >=1 existing digest endpoint; unknown ids, self-reference, and id collisions are loud), the digest prompt builder, and the runner (strong tier, retry-only). - extraction.ts: 'assembling' phase, spine stage in the per-need gate, usage/billing stage, assemble() inside withAuditLock for both front doors; first stretch of a source skips the call (empty digest, no spend); the audit sees COMBINED propositions (chunk + connectives), never chains; merger receives the combined findings. - core/graph.ts mergeFindings: chain endpoints resolve through the job-local map OR pass through verbatim as existing n_ ids (the spine's mixed namespace). - EXTRACTION_CORE: steps-of-reasoning selection bar (examples, notation glosses, and restatements that no argument needs are not steps). - Factory chunk budget 4096 -> 8192 (fewer seams, more local context). - healStringifiedArrays moved to the shared submit seam (submit-agent.ts). - Cost panel: Assembly row; usage records carry a spine stage (optional on legacy records). - Tests +5: prompt pins, span-law matrix, digest rendering, seeded end-to-end flow (spanning chain + connective, verbatim n_ endpoint, spine billed as its own stage), non-spanning refused + retried.
71 lines
2.0 KiB
TypeScript
71 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),
|
|
spine: stage(0), // spine channel exists since 2026-09-30 — zero spend here
|
|
audit: stage(0.75),
|
|
naming: stage(9.99, 2), // dead channel — must never reach the display
|
|
},
|
|
} as const;
|
|
|
|
it("sums the live stages 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);
|
|
});
|
|
});
|