diff --git a/README.md b/README.md index 89c36ab..51b9f68 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ data/ chats/ · graph/ (six ledgers + positions/source **The knowledge graph (two channels, one writer).** The write channel: the extraction pipeline — extractor (LLM: sees only the session, rendered as readable markdown by the shared `core/session-markdown.ts` engine — propositions as true statements, `follows`/`supports` edges with verbatim evidence; fully trusted, graph-blind) → auditor (LLM, an agentic loop that never sees the source: explores the existing graph through `find` / `list_sessions` / `list_propositions` tools, stamps each proposition with an identity verdict — link to an existing node (equivalence bias: paraphrases merge) or create — and may draw bridging edges between existing nodes and the new batch) → merger (`mergeFindings`, deterministic: applies the verdicts, link-appends phrasings/provenance, resolves and dedupes edges, checkpoints coverage) → layout settle. No quiz outcomes or understanding assessments live in the graph (2026-09-27 cleanup); practice (`/practice`) consumes the graph as feedback-only problem sets and writes nothing back. Triggered when a chat's **uncovered tokens** (message-view fingerprints, `coverage.jsonl`) cross `graph.extractionChunkTokens` (a settings knob, default 4096, floor 500 — read fresh per turn); failure checkpoints nothing, the next trigger re-runs. The same pipeline serves the **notes front door** (`POST /api/graph/import`: any `*.md`/`*.txt` becomes a source; re-imports are idempotent by path hash). The read channel: `GET /api/graph` derives the snapshot from the ledgers — a pure read; the home view is a hand-rolled Canvas 2D renderer (one rAF loop, dirty-flag idle, d3-zoom camera, zoom-gated labels, cluster hulls) over a d3-force layout, with server-persisted render settings and drag-committed positions. Clusters are **birth-session groupings**: a node joins the cluster of the session that birthed it (a chat, or an imported notes file as a file-kind session), derived at read — no embedding pass, no naming pass. The teacher reaches the graph on its own terms through the gated prober tools (`list_sessions` / `list_propositions`, the `agent.graphTools` setting) — there is no automatic context injection. -**Practice (problem sets).** The graph's `follows`-edges become reasoning chains; the `/practice` creation dialog picks a birth-session cluster (the graph view's own groups) and freezes its propositions + chains into a self-contained `ProblemSet`. Three named-level axes steer generation (ladder rung 0–4, cue distance 0–2, Theory|Application — never a number to interpolate; defaults Direct exercise · Adjacent context · Theory), plus an optional free-form note the agents weigh but need not follow. Creation has two entries: the cluster picker on `/practice`, or the graph view's chain table — a full-bleed sortable table of every reasoning chain, grouped by birth cluster under the shared top bar, selecting chips, chains, or whole clusters, whose floating selection bar shows the exact `N propositions → M chains` the agents will receive — handing a free-form node selection to the same dialog. A partitioner agent covers every chain exactly once, one generator per partition writes problems against the owner's problem-engineering framework (commit-before-pose: statement + solution + rubric together), and grading is **strengthening, not assessment** — one attempt per problem with its own submit button, instant solution/rubric reveal, then a score 0–10 with brief reasoning landing live; nothing ever written back to the graph. Runs are registry-backed background jobs on the activity dock (no steer, never wakes the teacher); sets live in `data/practice`, crash-swept on boot. +**Practice (problem sets).** The graph's `follows`-edges become reasoning chains; the `/practice` creation dialog picks a birth-session cluster (the graph view's own groups) and freezes its propositions + chains into a self-contained `ProblemSet`. Three named-level axes steer generation (ladder rung 0–4, cue distance 0–2, Theory|Application — never a number to interpolate; defaults Direct exercise · Adjacent context · Theory), plus an optional free-form note the agents weigh but need not follow. Creation has two entries: the cluster picker on `/practice`, or the graph view's chain table — a full-bleed sortable table of every reasoning chain, grouped by birth cluster under the shared top bar, selecting chips, chains, or whole clusters, whose floating selection bar shows the exact `N propositions → M chains` the agents will receive — handing a free-form node selection to the same dialog — and carries the fenced Delete action, making it the graph's management view (whole-cluster cleanup through the same full-death confirm dialog). A partitioner agent covers every chain exactly once, one generator per partition writes problems against the owner's problem-engineering framework (commit-before-pose: statement + solution + rubric together), and grading is **strengthening, not assessment** — one attempt per problem with its own submit button, instant solution/rubric reveal, then a score 0–10 with brief reasoning landing live; nothing ever written back to the graph. Runs are registry-backed background jobs on the activity dock (no steer, never wakes the teacher); sets live in `data/practice`, crash-swept on boot. **Dictation.** Optional speech-to-text: the server mints short-lived Deepgram tokens; audio streams browser→Deepgram over WebSocket, never through the server. diff --git a/components/chain-table.tsx b/components/chain-table.tsx index 69a7f06..3320360 100644 --- a/components/chain-table.tsx +++ b/components/chain-table.tsx @@ -51,8 +51,13 @@ * the Google-Photos pattern — appears only when something is selected, * showing the agents' ACTUAL input (chainsForSelection over the flat * node-id selection: a premise without its conclusion counts as the - * singleton it becomes) + Clear + Generate problem set (the one action - * for now; the bar is a neutral action slot for future ones). The fuzzy + * singleton it becomes) + Clear + Generate problem set. DELETE (owner, + * 2026-09-29 night: "this is really the view deletion should be done + * in") rides the same bar FENCED at the right end — divider + the + * destructive red pair — and opens the SAME full-death confirm dialog + * the canvas uses (multi-selections list their names + the unioned + * impact); this ends the overlay's read-only stance: it is the graph's + * MANAGEMENT surface now. The fuzzy * search (names, aliases, statements) filters chains grouped + * auto-expanded under their clusters (non-matching clusters hide; the * collapsed view returns on clear). @@ -68,6 +73,7 @@ import { ChevronUpIcon, FolderIcon, FolderOpenIcon, + Trash2Icon, ListTreeIcon, SearchIcon, XIcon, @@ -76,6 +82,7 @@ import { chainsForSelection, formatChainLine, fuzzyScore } from "@/src/core"; import type { GraphEdge, GraphNode } from "@/src/core"; import { TopBar } from "@/components/top-bar"; import { PracticeDialog } from "@/components/practice-dialog"; +import { DeleteNodeDialog } from "@/components/delete-node-dialog"; /** The sortable columns (owner ruling: all three — consistency). */ export type ChainSortCol = "name" | "props" | "created"; @@ -117,6 +124,7 @@ export function ChainTableBody({ onToggleNodes, onClear, onGenerate, + onDelete, onClose, }: { nodes: GraphNode[]; @@ -137,6 +145,8 @@ export function ChainTableBody({ onToggleNodes: (ids: string[], next: boolean) => void; onClear: () => void; onGenerate: () => void; + /** Opens the full-death confirm dialog (owner, 2026-09-29 night). */ + onDelete: () => void; onClose: () => void; }) { const byId = useMemo(() => new Map(nodes.map((n) => [n.id, n])), [nodes]); @@ -484,6 +494,21 @@ export function ChainTableBody({ > Generate problem set + {/* The danger fence (owner ruling): a divider quarantines + the destructive action at the bar's right end — the + safe actions and the kill switch never blend. The + full-death confirm dialog catches the misclick. */} +