delete the batch practice-problems module (superseded by practice sessions)

Owner ruling 2026-10-04: the module is a sunk cost, retrievable from
history if ever revisited. Removed: core practice contracts + pipeline
helpers, the PracticeStore port + adapter + data dir path, server
service + API routes (sets, clusters picker, practice aborts), the
practice pages + dialog/exam/list UI, chain-table's Generate handoff,
sidebar nav, activity-dock practice rows, and the partitioner/
problemGenerator/grader model roles (BACKGROUND_MODEL_ROLES 8 → 5).

Survivors, moved to core/chains.ts (pure + browser-safe): ReasoningChain,
chainsForSelection, formatChainLine — the chain table and graph
emission still consume them.
This commit is contained in:
amosblomqvist
2026-10-04 21:34:24 +03:00
parent a2c1287415
commit 814c2ad412
50 changed files with 125 additions and 5767 deletions
+3 -13
View File
@@ -17,7 +17,7 @@
* Extraction abort is idempotent by design (aborting with nothing in
* flight is success — the blob is gone either way) -> 200.
*/
import { abortExtraction, abortJob, abortTurn, practice } from "@/src/server";
import { abortExtraction, abortJob, abortTurn } from "@/src/server";
export async function POST(req: Request) {
let body: unknown;
@@ -27,11 +27,9 @@ export async function POST(req: Request) {
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
}
const { kind, id } = (body ?? {}) as { kind?: string; id?: string };
if ((kind !== "job" && kind !== "extraction" && kind !== "turn" && kind !== "practice") || typeof id !== "string" || id === "") {
return Response.json({ error: 'Body must be {kind: "job"|"extraction"|"turn"|"practice", id: string}' }, { status: 400 });
if ((kind !== "job" && kind !== "extraction" && kind !== "turn") || typeof id !== "string" || id === "") {
return Response.json({ error: 'Body must be {kind: "job"|"extraction"|"turn", id: string}' }, { status: 400 });
}
// Practice row keys carry ":" (setId:gen:problemId) — the charset
// admits it for every kind (job/turn/extraction ids never use it).
if (!/^[A-Za-z0-9:_-]+$/.test(id)) {
return Response.json({ error: "Invalid id" }, { status: 400 });
}
@@ -51,14 +49,6 @@ export async function POST(req: Request) {
}
// kind === "extraction": true cancellation; idempotent (nothing in
// flight is success — same dock outcome)
if (kind === "practice") {
// practice restructure (owner, 2026-09-29): id is the ROW key —
// abort routes by shape (a gen row kills THAT problem only; the
// partition row ends the run; a grade row drops that grade).
// Idempotent like extraction (nothing in flight is success).
practice.abortPracticeRow(id);
return Response.json({ ok: true });
}
abortExtraction(id);
return Response.json({ ok: true });
}
-11
View File
@@ -1,11 +0,0 @@
/**
* GET /api/graph/clusters — the birth-session groups for the practice
* creation picker (owner ruling 2026-09-29: sets are created from graph
* clusters, node ids only). Thin transport; the grouping is the graph
* view's own (server/graph.ts → graphSnapshot's clusters).
*/
import { graphClusters } from "@/src/server";
export async function GET() {
return Response.json({ clusters: graphClusters() }, { headers: { "cache-control": "no-store" } });
}
@@ -1,20 +0,0 @@
/**
* POST /api/practice/sets/[id]/answers — submit answers for grading
* (202; one grading job per answered problem runs in the background).
* Body: {answers: [{problemId, text}]}. Set must be ready; answers must
* be non-empty text on known, not-yet-answered problems (400/409 via the
* service's typed-error mapping). ONE ATTEMPT (owner 2026-09-29): once a
* problem carries an answer it is done — re-submission is refused.
*/
import { submitPracticeAnswersResponse } from "@/src/server";
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
let body: unknown;
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
}
return submitPracticeAnswersResponse(id, body);
}
-29
View File
@@ -1,29 +0,0 @@
/**
* GET /api/practice/sets/[id] — one problem set (the exam page polls
* this while status ≠ ready; graph-view's reveal-poll idiom).
* DELETE — abort the set's live jobs, then drop the record (the unified
* delete doctrine: no orphans, no zombies). 404 when never existed.
*/
import { getPracticeSet, practice, practiceErrorResponse } from "@/src/server";
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
try {
const set = getPracticeSet(id);
if (!set) return Response.json({ error: "no practice set", code: "not_found" }, { status: 404 });
return Response.json({ set });
} catch (err) {
return practiceErrorResponse(err);
}
}
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
try {
const existed = await practice.deleteProblemSet(id);
if (!existed) return Response.json({ error: "no practice set", code: "not_found" }, { status: 404 });
return Response.json({ ok: true });
} catch (err) {
return practiceErrorResponse(err);
}
}
-28
View File
@@ -1,28 +0,0 @@
/**
* POST /api/practice/sets — create a problem set (202 {id}; generation
* runs in the background — the fire-and-forget pipeline). Body:
* {nodeIds, params, title?, note?} — a node selection, the cluster's
* label, and the learner's optional
* free-form note
* Validation + typed-error mapping live in the service (server/practice).
* GET — the set list (createdAt desc, the store's order).
*/
import { createPracticeSetResponse, listPracticeSets, practiceErrorResponse } from "@/src/server";
export async function POST(req: Request) {
let body: unknown;
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
}
return createPracticeSetResponse(body);
}
export async function GET() {
try {
return Response.json({ sets: listPracticeSets() });
} catch (err) {
return practiceErrorResponse(err);
}
}