practice fix — creation is graph-domain: cluster picker, nodeIds-only API (owner ruling 2026-09-29)
Owner ruling: 'problem sets always take in nodes — graph objects' —
practice is graph-domain, not chat-domain. No session-created sets.
API: POST /api/practice/sets takes {nodeIds, params, title?} ONLY —
the chatId branch, UnknownChatError, and the any-provenance filter are
deleted; the selector is nodeIds (readNodes filtered to the selection,
unknown ids drop, empty live intersection → 400 empty_selection);
title optional from the UI (the picker's cluster label), dated default
stays. chatStore leaves the pipeline deps entirely (labels resolve
store-side in the clusters service).
GET /api/graph/clusters (new): the birth-session groups the graph view
renders — graphSnapshot's clusters verbatim (same ordering, same
MIN_CLUSTER_SIZE presentation filter), mapped to {key, kind
chat|notes, label (null when the naming record is gone — honest,
never guessed), nodeIds, nodeCount}; corrupt/empty graph → empty
list. GraphClusterGroup contract in core/contracts/graph.ts (client-
safe — the dialog imports it).
practice-dialog: session picker → cluster picker (rows show label +
proposition count, notes clusters marked); Create posts the cluster's
nodeIds + label as title + params. 400 empty-selection copy reworded
for the vanished-nodes edge ('Nothing practicable is left in that
cluster — the graph changed.'); 503 deep link unchanged. Preselected
mode gone — the dialog is the only creation entry for now (the graph-
view cluster entry comes later with free-form selection).
Sidebar: session right-click 'Practice this session's propositions'
removed (item + state + dialog wiring); the /practice nav row stays.
Tests: pipeline — chatId/any-provenance tests die, nodeIds selection
pins added (unknown-id filtering, dated-default title, title hint
rides, empty/vanished → EmptySelectionError); render — cluster picker
pins (label+count rows, notes kind, unlabeled cluster muted, empty
graph line), preselected pins removed; 16 render + 13 pipeline.
Docs: architecture.md §7 API+UI paragraph rewritten (graph-domain
ruling, /api/graph/clusters, cluster picker as the only entry, no
404-unknown-chat); README creation-flow line updated.
1130/1130, tsc 0, lint 0, build 0.
This commit is contained in:
+19
-1
@@ -1,6 +1,6 @@
|
||||
import * as fs from "node:fs";
|
||||
import { LEDGER_FILES, POSITIONS_FILE } from "../../config.ts";
|
||||
import type { GraphSnapshot } from "../core/contracts.ts";
|
||||
import type { GraphClusterGroup, GraphSnapshot } from "../core/contracts.ts";
|
||||
import type { GraphCostSummary } from "../core/cost-summary.ts";
|
||||
import { graphCostSummary } from "../core/cost-summary.ts";
|
||||
import { graphSnapshot } from "../core/graph.ts";
|
||||
@@ -23,6 +23,24 @@ export function graphView(): GraphSnapshot {
|
||||
return graphSnapshot(stores);
|
||||
}
|
||||
|
||||
/** GET /api/graph/clusters — the birth-session groups the graph view
|
||||
* renders, resolved to node ids for the practice picker (owner ruling
|
||||
* 2026-09-29: cluster→nodeIds resolves in the UI; the API stays
|
||||
* graph-domain). Reuses the snapshot's clustering verbatim — same
|
||||
* ordering (size desc, ties by id) and the same MIN_CLUSTER_SIZE
|
||||
* presentation filter the canvas honors. A corrupt/empty graph is an
|
||||
* empty list. */
|
||||
export function graphClusters(): GraphClusterGroup[] {
|
||||
const snap = graphSnapshot(stores);
|
||||
return snap.clusters.map((cluster) => ({
|
||||
key: cluster.id,
|
||||
kind: cluster.id in snap.sessions ? "chat" : "notes",
|
||||
label: cluster.label,
|
||||
nodeIds: cluster.members,
|
||||
nodeCount: cluster.members.length,
|
||||
}));
|
||||
}
|
||||
|
||||
/** The graph view's cost surface (GET /api/graph/costs — the 2026-09 cost
|
||||
* pill): fold every usage channel into stage totals + recent runs. A pure
|
||||
* read over the chat records (chat-front-door runs), the notes sources
|
||||
|
||||
Reference in New Issue
Block a user