The all-sessions coverage GET dies: the per-session block is the coverage surface
Extraction-UX close-out interlude (master dispatch 2026-09-24, off UI's ff1ec7a confirmation): nothing client-side consumes GET /api/graph/coverage — the sidebar block and the chat-bar ring both read GET /api/chats/[id]'s coverage block. The dead route (app/api/graph/coverage/route.ts) is deleted; the manual-extract front door (POST /api/graph/coverage/extract) stays by design. No server helper was orphaned — the route was composition-only over shared seams (chatStore, extractionStatuses, stores, coverageOf). The sessionCoverage doc + the chats/[id] route comment now point at the per-session block as THE coverage surface. Pin (tests/coverage.test.ts): the route file's absence (prompt-overrides' structural-pin precedent) + the Extract POST's survival — a merge cannot resurrect the dead surface silently.
This commit is contained in:
@@ -7,8 +7,9 @@ export async function GET(_req: Request, { params }: { params: Promise<{ id: str
|
||||
// turnInFlight lets a returning client re-attach to a running turn
|
||||
// (chat-handler write-ahead + registry — see src/server/turn-liveness.ts).
|
||||
// coverage (extraction-UX redesign, 2026-09-24): the chat view's ring
|
||||
// meter — one session's read of the same derivation /api/graph/coverage
|
||||
// serves for all sessions. getChat already 404'd, so the block exists.
|
||||
// meter + sidebar block — THE coverage surface (the all-sessions GET
|
||||
// route died with the redesign; the Extract POST stays). getChat
|
||||
// already 404'd, so the block exists.
|
||||
return Response.json({ ...chat, turnInFlight: isTurnRunning(id), coverage: sessionCoverage(id) });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* GET /api/graph/coverage — the exact extraction coverage of every session
|
||||
* (vision-extraction-coverage.md, settled 2026-09-15): which sessions have
|
||||
* unextracted content, how much (tokens + message counts), and which runs
|
||||
* are in flight. "Uncovered" is DERIVED here per request — fingerprint the
|
||||
* current transcript, diff against the coverage ledger — never tracked as
|
||||
* a second ledger. This is the data behind the graph view's coverage panel
|
||||
* and its per-session "extract what's missing" buttons.
|
||||
*/
|
||||
import type { UIMessage } from "ai";
|
||||
import { chatStore, extractionStatuses, stores } from "@/src/server";
|
||||
import { coverageOf } from "@/src/core";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET() {
|
||||
const running = new Set(
|
||||
extractionStatuses().filter((run) => run.kind === "chat").map((run) => run.chatId),
|
||||
);
|
||||
const entries = stores.graphStore.readCoverage();
|
||||
const chats = chatStore.listChats().map((summary) => {
|
||||
const record = chatStore.getChat(summary.id);
|
||||
const messages = (record?.messages ?? []) as UIMessage[];
|
||||
const cov = coverageOf(messages, entries, summary.id);
|
||||
return {
|
||||
chatId: summary.id,
|
||||
title: summary.title,
|
||||
updatedAt: summary.updatedAt,
|
||||
totalMessages: cov.slots.length,
|
||||
coveredMessages: cov.coveredCount,
|
||||
uncoveredMessages: cov.uncoveredCount,
|
||||
coveredTokens: cov.coveredTokens,
|
||||
uncoveredTokens: cov.uncoveredTokens,
|
||||
running: running.has(summary.id),
|
||||
};
|
||||
});
|
||||
// Most uncovered first — the actionable sessions lead the panel.
|
||||
chats.sort((a, b) => b.uncoveredTokens - a.uncoveredTokens || (a.updatedAt < b.updatedAt ? 1 : -1));
|
||||
return Response.json({ chats });
|
||||
}
|
||||
Reference in New Issue
Block a user