Wave 7 close-out: the embedding ledger seam dies; notes birth via mapping lines
Part 1 — the ledger + remnants interlude (master-dispatched, owner ruling 2026-09-23: nothing configures the graph beyond chat models): - The embedding ledger surface is gone: readEmbeddings/writeEmbeddings ports, the EmbeddingsLedger contract, EMBEDDINGS_FILE (config, store impls, graphRevision source list), and graph-deletion's vector-pruning step. On-disk data files stay untouched (inert history). - The extraction embedder seam dies wholesale: ExtractionPipelineDeps drops the embedder dep (chat-handler + backfill/import-notes stubs go with it), the 'embedding' phase leaves ExtractionPhase, the usage capture/persist helpers die, and stages.embedding leaves ExtractionRunUsage (historical records keep their shape — reads are lenient). Extraction is two stages again, like the local-embedder era. - Consumer-less remnants: latestQuizOutcomes + QUIZ_EVENT_KINDS, clusterContentId, matchClusterLabelings + Community + ClusterLabeling, the readClusterLabels/appendClusterLabel port seams + store impls + LEDGER_FILES.clusterLabels. NamingRunUsage/readNamingRuns STAY (standing ruling — the cost pill renders historical naming spend); clusterHue + MIN_CLUSTER_SIZE stay (live). - The 'labeler' role dies: BACKGROUND_MODEL_ROLES, ROLE_SLOT, ModelRole, the catalog pin projections, and the UI roster strings. RolePins keys shrink with the union. The notes birth fallback (owner-approved 2026-09-23, surfaced by the interlude's live smoke: production notes imports create provenance-EMPTY nodes (importNotes passes messages: null — the file's anchor is the mapping line), so the provenance-only birth rule left every imported file unclustered and invisible to the prober's session list, against the locked file-birth ruling. A provenance-empty node now births under its FIRST mapping line's chatId (ledger order); only notes-born nodes ever hit the fallback (chat nodes always carry provenance; a re-encountered node's link-append re-opens the provenance rule). Deletion-safe: mapping lines of deleted chats are stripped alongside their provenance. Live-verified on port 3004: a fresh notes import clusters under its src_ id with the file path as the label, end to end with no embedder. 845/845 tests, typecheck/lint/build green. EOF )
This commit is contained in:
@@ -39,6 +39,7 @@ function fakeStores(
|
||||
events?: LedgerEvent[];
|
||||
chats?: Array<Pick<ChatSummary, "id" | "title" | "createdAt" | "updatedAt">>;
|
||||
sources?: Array<Pick<NotesSource, "id" | "filePath" | "importedAt">>;
|
||||
mappings?: Array<{ chatId: string; sourceId: string; graphNodeId: string }>;
|
||||
} = {},
|
||||
): GraphDeps {
|
||||
const edges: GraphEdge[] = [];
|
||||
@@ -46,18 +47,13 @@ function fakeStores(
|
||||
readNodes: () => nodes,
|
||||
readEdges: () => edges,
|
||||
readEvents: () => over.events ?? [],
|
||||
readMappings: () => [],
|
||||
readMappings: () => over.mappings ?? [],
|
||||
readCheckpoints: () => [],
|
||||
readCoverage: () => [],
|
||||
readSources: () =>
|
||||
(over.sources ?? []).map((s) => ({ ...s, extractions: [] }) as unknown as NotesSource),
|
||||
readPositions: () => ({}),
|
||||
writePositions: () => {},
|
||||
readEmbeddings: () => ({ model: "dead", dim: 0, vectors: {}, clusters: [], updatedAt: "" }),
|
||||
writeEmbeddings: () => {},
|
||||
readClusterLabels: () => [],
|
||||
appendClusterLabel: () => {},
|
||||
appendNamingRun: () => {},
|
||||
readNamingRuns: () => [],
|
||||
} as unknown as GraphStore;
|
||||
const chatStore: ChatStore = {
|
||||
@@ -110,6 +106,80 @@ describe("birthSessionPartition", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── the notes mapping fallback (owner-approved 2026-09-23) ─────────────────
|
||||
|
||||
describe("the notes mapping-line fallback", () => {
|
||||
// The PRODUCTION notes shape: importNotes passes messages: null, so
|
||||
// nodes are born with EMPTY provenance — the file's anchor is the
|
||||
// mapping line (interlude live-smoke finding: the provenance-only rule
|
||||
// left every imported file unclustered and invisible to the prober).
|
||||
it("empty-provenance nodes birth under their first mapping line's session", () => {
|
||||
const nodes = [
|
||||
node("a", "Alpha", []),
|
||||
node("b", "Beta", []),
|
||||
node("c", "Gamma", []),
|
||||
node("d", "Delta", [pt("chat1")]), // provenance wins — mapping ignored
|
||||
];
|
||||
const mappings = [
|
||||
{ chatId: "src_notes", sourceId: "p1", graphNodeId: "a" },
|
||||
{ chatId: "src_notes", sourceId: "p2", graphNodeId: "b" },
|
||||
{ chatId: "src_notes", sourceId: "p3", graphNodeId: "c" },
|
||||
// A chat mapping for a node that ALSO has provenance: never consulted.
|
||||
{ chatId: "chat2", sourceId: "p4", graphNodeId: "d" },
|
||||
// A mapping for a node that does not exist: ignored.
|
||||
{ chatId: "src_notes", sourceId: "p5", graphNodeId: "ghost" },
|
||||
];
|
||||
const clusters = birthSessionPartition(nodes, mappings);
|
||||
assert.deepEqual(clusters, [
|
||||
{ id: "src_notes", members: ["a", "b", "c"] },
|
||||
{ id: "chat1", members: ["d"] },
|
||||
], "size-desc, then id asc — src_notes first");
|
||||
// No mapping and no provenance: unclustered.
|
||||
assert.deepEqual(birthSessionPartition([node("x", "X", [])], mappings), []);
|
||||
});
|
||||
|
||||
it("graphSessionIndex lists imported files as kind:file with birth counts (production shape)", () => {
|
||||
const nodes = [node("a", "Alpha", []), node("b", "Beta", []), node("c", "Gamma", [])];
|
||||
const deps = fakeStores(nodes, {
|
||||
mappings: [
|
||||
{ chatId: "src_abc", sourceId: "p1", graphNodeId: "a" },
|
||||
{ chatId: "src_abc", sourceId: "p2", graphNodeId: "b" },
|
||||
{ chatId: "src_abc", sourceId: "p3", graphNodeId: "c" },
|
||||
],
|
||||
sources: [{ id: "src_abc", filePath: "/vault/learn vim.md", importedAt: "2026-09-22T00:00:00.000Z" }],
|
||||
});
|
||||
const index = graphSessionIndex(deps);
|
||||
assert.deepEqual(index, [
|
||||
{
|
||||
id: "src_abc",
|
||||
name: "/vault/learn vim.md",
|
||||
kind: "file",
|
||||
createdAt: "2026-09-22T00:00:00.000Z",
|
||||
updatedAt: "2026-09-22T00:00:00.000Z",
|
||||
propositions: 3,
|
||||
},
|
||||
], "the file births under its src id; the childless chat has no entry (birth sessions only)");
|
||||
});
|
||||
|
||||
it("graphSessionPropositions lists the file's births under the src id", () => {
|
||||
const events: LedgerEvent[] = [
|
||||
{ nodeId: "a", kind: "observation", chatId: "src_abc", at: "2026-09-22T00:00:00.000Z", note: "solid", ref: "obs:p1" },
|
||||
];
|
||||
const deps = fakeStores([node("a", "Alpha", []), node("b", "Beta", [])], {
|
||||
events,
|
||||
mappings: [
|
||||
{ chatId: "src_abc", sourceId: "p1", graphNodeId: "a" },
|
||||
{ chatId: "src_abc", sourceId: "p2", graphNodeId: "b" },
|
||||
],
|
||||
sources: [{ id: "src_abc", filePath: "/vault/learn vim.md", importedAt: "2026-09-22T00:00:00.000Z" }],
|
||||
});
|
||||
assert.deepEqual(graphSessionPropositions(deps, "src_abc"), [
|
||||
{ nodeId: "a", name: "Alpha", understanding: "solid", evaluatedAt: "2026-09-22T00:00:00.000Z" },
|
||||
{ nodeId: "b", name: "Beta", understanding: null, evaluatedAt: null },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── the snapshot join ──────────────────────────────────────────────────────
|
||||
|
||||
describe("graphSnapshot birth-session clusters", () => {
|
||||
|
||||
Reference in New Issue
Block a user