extraction cleanup: events ledger dies, markdown source view, auditor-as-linker (2026-09-27 rulings)
The graph becomes raw reasoning chains only. Quiz outcomes and understanding observations are no longer formalized; assessment moves to the future practice module. Events ledger removed end-to-end: LedgerEvent/LedgerEventKind/ UnderstandingLevel/ExtractionAttribution/AuditResult.dropped and snapshot nodeObservations deleted from contracts; quizEvent builder and the events step of mergeFindings gone; appendEvent/readEvents dropped from the port and store; LEDGER_FILES loses events.jsonl with a one-time purge of a stale file next to the nodes ledger; graph-deletion and the delete dialog lose event purge + quiz-answer impact counts; GraphSessionProposition becomes a pure birth list. Whole-session markdown source view: new core/session-markdown.ts is the shared formatting engine (id-anchored speaker blocks, quiz/ recall/question blocks, tool one-liners) — the extractor's prompt view and the future user-facing export render from one engine. Coverage fingerprints deliberately stay on the old line format (content-hash semantics; re-formatting never re-opens covered history). The whole session goes to the extractor — no phase cut. Auditor refocused as the identity linker: never sees the source; explores the graph via new auditor-specific tools in agent/tools/graph.ts (find — glob over name/statement/aliases; list_sessions; list_propositions with full statement text; no timestamps) instead of an injected full-graph dump; equivalence bias — paraphrases merge, only genuinely distinct propositions stay apart; no drop channel, the extractor is fully trusted; may draw bridging edges (typed follows/supports) between existing nodes and the batch, resolved by the merger with chat-scoped message-less provenance. Agentic loop, maxTurns 16. UI: the node detail card gains an edges section above the sessions list (follows-from / supported-by / what-follows / what-it-supports, rendered only when present, click-to-select neighbors); the understanding-note chips are gone. Prober list_propositions reworded to what a session covered — names only, no levels. docs/architecture.md + README updated to the as-built pipeline. Gates: typecheck, 1059/1059, lint, build.
This commit is contained in:
+18
-22
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* GraphStore — the fs JSONL adapter: five append-only ledgers at
|
||||
* GraphStore — the fs JSONL adapter: four append-only ledgers at
|
||||
* config.LEDGER_FILES (contracts.ts "v1 emission").
|
||||
* nodes.jsonl GraphNode — one line per node write: the FIRST
|
||||
* line for an id defines it; a later
|
||||
@@ -10,7 +10,6 @@
|
||||
* edges.jsonl GraphEdge — one line per edge ASSERTION (a second
|
||||
* session asserting the same (from,to,type)
|
||||
* appends another line; reads merge)
|
||||
* events.jsonl LedgerEvent — quiz outcomes + understanding observations
|
||||
* mappings.jsonl NodeMapping — the session→graph identity map:
|
||||
* (chatId, sourceId) → graphNodeId
|
||||
* sessions.jsonl SessionCheckpoint— emission checkpoints (foldedAt)
|
||||
@@ -34,8 +33,6 @@ import type {
|
||||
GraphEdge,
|
||||
GraphNode,
|
||||
ExtractionRunUsage,
|
||||
LedgerEvent,
|
||||
LedgerEventKind,
|
||||
NotesSource,
|
||||
NodeMapping,
|
||||
Position,
|
||||
@@ -110,15 +107,6 @@ const isGraphEdge = (v: unknown): v is GraphEdge =>
|
||||
Array.isArray(v.provenance) &&
|
||||
v.provenance.every(isProvenancePoint);
|
||||
|
||||
const EVENT_KINDS: readonly LedgerEventKind[] = ["correct", "miss", "dont-know", "observation"];
|
||||
|
||||
const isLedgerEvent = (v: unknown): v is LedgerEvent =>
|
||||
isRecord(v) &&
|
||||
(v.nodeId === null || isString(v.nodeId)) &&
|
||||
(EVENT_KINDS as readonly string[]).includes(v.kind as string) &&
|
||||
isString(v.chatId) &&
|
||||
isString(v.at);
|
||||
|
||||
const isNodeMapping = (v: unknown): v is NodeMapping =>
|
||||
isRecord(v) && isString(v.chatId) && isString(v.sourceId) && isString(v.graphNodeId);
|
||||
|
||||
@@ -149,13 +137,6 @@ export const graphStore: GraphStore = {
|
||||
return readValid(LEDGER_FILES.edges, isGraphEdge);
|
||||
},
|
||||
|
||||
appendEvent(event: LedgerEvent): void {
|
||||
appendLine(LEDGER_FILES.events, event);
|
||||
},
|
||||
readEvents(): LedgerEvent[] {
|
||||
return readValid(LEDGER_FILES.events, isLedgerEvent);
|
||||
},
|
||||
|
||||
appendMapping(mapping: NodeMapping): void {
|
||||
appendLine(LEDGER_FILES.mappings, mapping);
|
||||
},
|
||||
@@ -217,7 +198,6 @@ export const graphStore: GraphStore = {
|
||||
rewriteLedgers(next: {
|
||||
nodes?: GraphNode[];
|
||||
edges?: GraphEdge[];
|
||||
events?: LedgerEvent[];
|
||||
mappings?: NodeMapping[];
|
||||
coverage?: CoverageEntry[];
|
||||
checkpoints?: SessionCheckpoint[];
|
||||
@@ -225,7 +205,6 @@ export const graphStore: GraphStore = {
|
||||
const files = {
|
||||
nodes: LEDGER_FILES.nodes,
|
||||
edges: LEDGER_FILES.edges,
|
||||
events: LEDGER_FILES.events,
|
||||
mappings: LEDGER_FILES.mappings,
|
||||
coverage: LEDGER_FILES.coverage,
|
||||
checkpoints: LEDGER_FILES.sessions,
|
||||
@@ -326,5 +305,22 @@ function writeSourceMap(map: Record<string, NotesSource>): void {
|
||||
fs.writeFileSync(SOURCES_FILE, JSON.stringify(map, null, "\t"), "utf-8");
|
||||
}
|
||||
|
||||
// ─── one-way migration: the events ledger is gone (2026-09-27) ────────────────
|
||||
// Quiz outcomes and understanding observations are no longer formalized in
|
||||
// the graph — the pipeline writes nodes + edges only. A stale events.jsonl
|
||||
// left by an earlier build is deleted once, here, at adapter load: dead
|
||||
// ledgers are exactly the confusion the cleanup removes (owner ruling).
|
||||
{
|
||||
const legacyEvents = path.join(path.dirname(LEDGER_FILES.nodes), "events.jsonl");
|
||||
try {
|
||||
if (fs.existsSync(legacyEvents)) {
|
||||
fs.rmSync(legacyEvents);
|
||||
console.info(`[graph-store] purged legacy events ledger: ${legacyEvents}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[graph-store] could not purge legacy events ledger:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
const isNotesSource = (v: unknown): v is NotesSource =>
|
||||
isRecord(v) && isString(v.id) && isString(v.filePath) && isString(v.importedAt);
|
||||
|
||||
Reference in New Issue
Block a user