feat: the sessionless draft view — "+ New session" creates nothing

The sidebar button no longer POSTs a record (no more "Untitled session"
litter): it opens /chat/new, a sessionless draft view visually identical
to a fresh chat (same header + probe chip, the same ConversationEmptyState,
the same composer blob + stats dock). The session comes to exist only
when the learner sends the first message — and that message becomes its
title (startDraftSession: POST /api/chats {title: msg} → PUT the lifted
pre-chat settings → redirect /chat/<id>?prompt=<msg>, whose initialPrompt
path delivers the message as turn 1 — the home composer's proven contract).

- app/chat/new/page.tsx: the static draft route (static beats [id]).
- components/new-session.tsx: DraftShell (pure render seam) + NewSession
  (router wiring); the model picker's empty-chatId branch defers its save
  and lifts the selection, so pre-chat model/thinking rides the creation.
- components/sidebar.tsx: newSession is a plain router.push("/chat/new").
- tests/render-new-session.test.tsx: 6 pins — the create-on-send contract
  (title = message, settings PUT, no-op on empty text) + the shell render.

Live-verified end to end: button → draft (0 creations), pre-chat Qwen
pick, send → exactly one session titled with the message, settings
persisted, redirect + turn 1 running. Gates 431/431.
This commit is contained in:
amosblomqvist
2026-09-14 09:06:48 +03:00
parent 7f001cdb46
commit 0f6e36f85f
4 changed files with 271 additions and 7 deletions
+14
View File
@@ -0,0 +1,14 @@
import { NewSession } from "@/components/new-session";
export const dynamic = "force-dynamic";
/**
* The draft view behind "+ New session" (owner, 2026-09-13): NO session is
* created until the first message is sent — the view is a sessionless empty
* chat; on send it creates the chat (title = that message) and redirects to
* the real page, whose initialPrompt path delivers the message as turn 1.
* Static segment beats the dynamic [id] route — "new" is never a chat id.
*/
export default function NewSessionPage() {
return <NewSession />;
}