feat(graph): two-mode session deletion — the first destructive ledger operation

DELETE /api/chats/[id]?mode=preserve-graph|delete-nodes (mode required):
- preserve-graph: only extraction bookkeeping dies (coverage rows,
  legacy checkpoints) — graph ledgers byte-untouched
- delete-nodes: the chat's ledger traces die. Node survival = surviving
  provenance point / mapping line (notes imports carry empty
  provenance) / surviving event. The creator's aliases + provenance are
  stripped from a surviving node's create line (owner ruling
  2026-09-23: name/statement/createdAt are the shared identity);
  orphan nodes, their edges, events, mappings, positions, embedding
  vectors and cluster members die; cluster labels stay (immutable)

Gates: 404 unknown, 409 turn_in_flight / jobs_running (with jobIds),
503 extraction_winding_down after a 10s abort-and-poll; the purge is
one sync block (ledgers → jobs → chat record last). rewriteLedgers is
atomic (tmp + rename). graphRevision (max mtime over graph data files)
rides the activity feed + the response as the change stamp.

Anti-resurrection: the chat pipeline re-checks chat liveness at each
merge — a run that slips past the wind-down aborts with nothing merged
or checkpointed; its partial spend dies with the chat.
This commit is contained in:
amosblomqvist
2026-09-23 02:41:04 +03:00
parent ff3700a2d0
commit a6338511d3
13 changed files with 856 additions and 5 deletions
+14
View File
@@ -129,6 +129,19 @@ function renameChat(id: string, title: string): ChatRecord {
return record;
}
/** Delete the session record file entirely (session-deletion wave 6,
* owner 2026-09-22). False when the id is unknown — the route turns that
* into a 404. The record's graph traces and jobs are the route's
* choreography (core/graph-deletion.ts), not the store's. */
function deleteChat(id: string): boolean {
try {
fs.unlinkSync(chatFile(id));
return true;
} catch {
return false; // never existed, already deleted, or unreadable — same answer
}
}
/** Persist the session plan tree (plan tools call this in execute). */
function saveTree(id: string, tree: PlanTree): void {
const record = getChat(id);
@@ -147,6 +160,7 @@ export const chatStore: ChatStore = {
saveTree,
saveSettings,
renameChat,
deleteChat,
appendTurnUsage,
appendExtractionUsage,
};