Owner 2026-09-30: the confirm dialog's pre-flight impact read had to
go — what full death takes is obvious, the per-open fetch flickered,
and richer facts would only confuse as deletion semantics evolve
(rewiring is on the table). The screen is now: Delete node? / Delete
N nodes?, the names in the mono block, one permanence line, Cancel /
Delete. No loading state, no gone state (a 404 mid-run lands in the
honest failure line), no sessions join — the joins prop died too.
The dead path is deleted end to end: GET /api/graph/nodes/[id],
graphNodeImpact (server), nodeDeletionImpact (core) and their tests.
WORDING: node/node(s) is THE UI word for graph objects, standardized
across every surface's copy (owner, same ruling): the dialog titles,
the chain table's counts/search/summary/delete aria-labels, the
practice dialog's cluster counts, selection line, and empty-cluster
hint. Internal identifiers (selection.propositions) unchanged.
Rewritten dialog pins (tests/delete-node-dialog.test.tsx): minimal
contract — no facts/loading/fetch copy, singular/plural titles, the
cap, 409 dock phrasing, busy states, no 'proposition' left in the
dialog.
The remedy for undeletable nodes (preserve-graph orphans from session
deletion; notes-import nodes with no chat behind them): DELETE
/api/graph/nodes/[id], full-death semantics — the user pointing at the
node IS the intent; references from other sessions do NOT preserve it.
- core/graph-deletion.ts deleteNodeTraces: the node's every line, every
edge touching it, every quiz/observation event naming it, its mapping
lines, and its resting position — one synchronous atomic rewriteLedgers
call; only the ledgers that changed rewrite (mtimes stay honest). Blast
radius is exactly the rows naming the node id; notes source records stay
(deleting a proposition is not deleting the file). Derived data
self-heals on read: the birth cluster shrinks below MIN_CLUSTER_SIZE and
stops rendering.
- nodeDeletionImpact (GET /api/graph/nodes/[id]): the confirm dialog's
pre-flight facts — edge count, quiz/observation history, referencing
sessions. The snapshot's edges + nodeSessions cover part of this, but
the raw event history was nowhere on the client — hence the read.
- Guards (server/graph.ts deleteGraphNode), decided + documented: NO
turn/jobs gates (they never write graph ledgers; the orchestration is
synchronous end to end, nothing interleaves). THE EXTRACTION GATE: 409
while ANY pipeline run is registered — a mid-flight run holds
pre-deletion findings and could re-reference the dying node. The run is
never aborted (its spend belongs to another session — outside this
deletion's blast radius; the UI retries once the dock drains. A run
started after the deletion is legitimate re-learning: fresh ledgers,
fresh node id, and mergeFindings' existingIds guard ignores linkTo ids
that no longer exist — no dangling edges, no resurrection.
Live-verified on dev data: impact read, full-death stats, honest 404 on
repeat, and the file cluster fell 3→2 and unrendered itself.
859/859 tests, typecheck/lint/build green.
EOF
)