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:
amosblomqvist
2026-09-23 15:37:34 +03:00
parent 1c3a5e121c
commit d1b46132b9
30 changed files with 181 additions and 595 deletions
+76 -6
View File
@@ -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", () => {