deletion screen minimal + 'node' as THE UI word (owner ruling)

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.
This commit is contained in:
amosblomqvist
2026-09-30 01:19:03 +03:00
parent 40dc1b7f85
commit e7f4923159
13 changed files with 179 additions and 401 deletions
-23
View File
@@ -260,33 +260,10 @@ export interface NodeDeletionStats {
edgesRemoved: number;
}
/** The confirm dialog's pre-flight facts (the GET on the same node id):
* what full death would take with it. sessions is the deduped set of
* chats/notes sources that reference the node — provenance points count. */
export interface NodeDeletionImpact {
edges: number;
sessions: string[];
}
/** A node exists iff a ledger line carries its id. */
function nodeLinesOf(graphStore: GraphStore, nodeId: string): GraphNode[] {
return graphStore.readNodes().filter((line) => line.id === nodeId);
}
/** The impact facts for one node, or null when no such node exists. */
export function nodeDeletionImpact(graphStore: GraphStore, nodeId: string): NodeDeletionImpact | null {
if (nodeLinesOf(graphStore, nodeId).length === 0) return null;
const edges = graphStore.readEdges().filter((line) => line.from === nodeId || line.to === nodeId);
const sessions = new Set<string>();
for (const line of nodeLinesOf(graphStore, nodeId)) {
for (const pt of line.provenance) sessions.add(pt.chatId);
}
return {
edges: edges.length,
sessions: [...sessions].sort(),
};
}
/**
* Full-death one node. Returns null when no such node exists (the
* orchestration maps that to 404); rewrites only the ledgers that