Skip to content

Scott chat: edit + resend a user message (erase subsequent turns with mid-conversation warning) #105

@rafacm

Description

@rafacm

Problem / motivation

Today, once a user has sent a message in a Scott conversation, the only way to refine the question is to send a follow-up. There's no way to:

  • Fix a typo and re-run the same turn.
  • Reframe an earlier question and have Scott re-answer with the same retrieval context.
  • "Branch off" a chosen point in a long conversation without starting a brand-new thread.

This is the standard edit & regenerate pattern shipped by ChatGPT, Claude.ai, and most modern chat UIs. Adding it would make iterative question-refinement (a common Scott workflow) much faster, and would also be useful for the PR #100 verification step "ask Scott a question that hits a freshly-enriched chunk" — being able to re-run a question after a background enrichment lands turns the verification into one click instead of typing a fresh thread.

UX

  1. Each user message bubble grows a pencil-icon button visible on hover (or always, on touch).

  2. Clicking it swaps the bubble's static text for an editable composer pre-filled with the original message.

  3. Two buttons under the editor: Save & resend and Cancel.

  4. If the edited message is the last user message in the thread, save & resend silently erases the assistant's reply (if present) and re-runs the agent from this turn.

  5. If the edited message is mid-conversation (i.e. there are messages after the assistant's reply to this turn), Save & resend opens a confirm dialog:

    Editing this message will erase the rest of the conversation (N more messages). Continue?

    On confirm: erase all subsequent user + assistant messages from the thread (Postgres + assistant-ui state), then re-run the agent from the edited turn. On cancel: stay in edit mode.

  6. Cancel restores the original message text and exits edit mode without changes.

What assistant-ui already gives us

  • MessagePrimitive.If type=\"user\" — for conditionally rendering edit affordance on user bubbles only.
  • MessageRuntime.edit() / useMessageRuntime() — the in-memory state transition; flips a message into edit mode.
  • ComposerPrimitive.Edit — the in-place edit composer; renders the pre-filled textarea + Save / Cancel buttons.
  • The runtime's existing reload-from-message semantics — when a user message is edited, downstream messages are dropped and the agent re-runs.

So the primary work is CSS + a confirmation dialog, not protocol-level surgery. The remote thread adapter (frontend/src/threadAdapter.ts) already round-trips messages to Postgres via chat/views.py:api_conversation_history (PUT/POST), so message deletion on edit will propagate to the server through the existing save-on-mutation hook.

Backend implications

Conversation / Message are stored as a tree (parent-id chains) in chat/models.py. The existing ExportedMessageRepository round-trip already handles partial trees on save. Likely no backend change required — when the edit causes assistant-ui to truncate the in-memory tree and resave, the API endpoint will accept the smaller tree and the pruned messages become orphaned (or get cleaned up depending on the replace=True semantics already in api_conversation_history).

If we want a stronger guarantee, add an idempotent "replace from message X onward" endpoint, but that's only worth it if the in-place save proves lossy in testing.

Implementation sketch

  1. frontend/src/chat.tsx — extend UserMessage() to render an Edit button, gated behind MessagePrimitive.If type=\"user\". Wire its onClick to useMessageRuntime().composer.beginEdit().
  2. New EditComposer component using ComposerPrimitive.Edit for the textarea + Save / Cancel buttons.
  3. Branch the Save handler:
    • Walk the thread state to find messages strictly after this turn.
    • If count > 0, open a Radix-style <AlertDialog> (already a Tailwind 4 / shadcn-style dependency in this project).
    • On confirm, call composer.send() (which assistant-ui implements as edit + truncate + rerun).
  4. Style the pencil icon as the existing chat aesthetic (warm-brown primary, hover-only on desktop, always-on for touch).
  5. Tests: a Playwright spec under chat/tests.py (or frontend/) that covers (a) edit last message → no dialog, (b) edit mid-message → dialog → confirm → tree truncates, (c) cancel restores text.

Out of scope

  • Conversation branching / forking (keep both the original and the edited branch, with a UI to switch). Cleaner long-term but heavier; skip for v1.
  • Edit assistant messages. Keeping user-only is the standard pattern and avoids correctness implications for citation indices.
  • Edit history (audit trail of past versions of an edited message). Nice-to-have; only worth it if anyone asks.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions