fix: reject empty prose on turn submit

Empty submission was producing a blank user_turn event in the log and
firing the LLM stream anyway — the bot would invent a response from the
kickoff context alone, producing a monologue with no user input. Two-
layer fix:

- Browser: add `required` to the prose textarea in chat.html so the
  form refuses to submit empty.
- Server: 400 in post_turn when prose.strip() is empty. Defense in
  depth — if a client bypasses the textarea attribute (custom UI,
  curl, etc.), the server still rejects.

Verified live: POST with empty body returns 400; POST with whitespace-
only returns 400; chat shell renders the textarea with required.
Full suite: 168 passed.
This commit is contained in:
Joseph Doherty
2026-04-26 15:20:02 -04:00
parent 5c039c8e56
commit 52555e0455
2 changed files with 4 additions and 1 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
</section>
<form class="turn-input" method="post" action="/chats/{{ chat.id }}/turns">
<textarea name="prose" rows="3" placeholder="What do you say or do?"></textarea>
<textarea name="prose" rows="3" placeholder="What do you say or do?" required></textarea>
<button type="submit">Send</button>
</form>
+3
View File
@@ -122,6 +122,9 @@ async def post_turn(
conn=Depends(get_conn),
client=Depends(get_llm_client),
):
if not prose.strip():
raise HTTPException(status_code=400, detail="prose cannot be empty")
chat = get_chat(conn, chat_id)
if chat is None:
raise HTTPException(status_code=404, detail=f"chat not found: {chat_id}")