Skip to content

feat(conv): review-queue read model, internal notes, draft reject stamps - #865

Open
kmonsoe wants to merge 1 commit into
mainfrom
feat/conv-review-queue-read-model
Open

feat(conv): review-queue read model, internal notes, draft reject stamps#865
kmonsoe wants to merge 1 commit into
mainfrom
feat/conv-review-queue-read-model

Conversation

@kmonsoe

@kmonsoe kmonsoe commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

First slice of the Oversight console rebuild (per OVERSIGHT-HANDOVER.md — supersedes the closed #858#863 stack). Backend read/write model the review queue sits on; no dashboard changes yet.

What's here

  • GET /v1/conversations/queue — the list DTO the handover's item 1 asked for: left-joins conv_contacts, conv_topics, conv_channels and returns customerName/customerEmail, topicName/topicSlug, channelType, the active claim (holderId, holderName, expiresAt), noteCount, and hasPendingDraft. Claim + note stats load as two batched queries over the page, not per-row subqueries.
  • Internal notes (handover item 3) — notes are conv_messages rows: internal: true, metadata.kind = 'internal_note' (stamped by sendMessage for user/agent internal messages). Internal messages no longer bump last_message_at, so recording context can't reorder the queue. New conversation.note_added event keeps other operators' panes live.
  • Reject leg of the draft audit trailclearDraftReply stamps draft_reply_rejected (+ rejectedByUserId, rejectedAt) instead of hard-deleting; a draft replaced by a newer one is stamped draft_reply_superseded with the new draft's id. draft_reply_sent now merges into the draft's metadata so retrievedDocumentIds survives approval.
  • GET /v1/orgs/me/roster (handover item 6) — members + active claim counts, readable by any org member (unlike the owner/admin-gated members endpoint). Feeds "On duty now".
  • POST /v1/conversations/:id/request-draft — the design's "Ask for a draft": pre-checks (open, non-voice, end-user bound, agent not off, no pending draft → 409 conv_draft_pending) then emits conversation.draft_requested. The runner-side handling ships with the rationale PR next. setDraftReply now emits conversation.draft_ready so the queue refreshes when a requested draft lands.
  • Event catalog + subscribe-to-events skill updated with the three new conv events.

Tests

  • 12 new cases in conv.service.test.ts (queue enrichment, end-user fallback, note no-bump/no-reorder invariant, note stamping + events, attention preserved, reject stamp, supersede, draft_ready, request-draft happy/conflict/closed) + roster controller tests (member access, api-key viewer).
  • pnpm vitest run src/modules/conv src/control src/modules/webhooks: 615 passed. @getmunin/types: 82 passed. Typecheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CPwvC1MqF1uhpNqC8BmvQN

GET /v1/conversations/queue enriches rows with customer, topic, channel
type, active claim, note count and pending-draft flag. Internal notes
stamp metadata.kind=internal_note and internal messages no longer bump
last_message_at. clear-draft stamps draft_reply_rejected instead of
deleting; replaced drafts stamp draft_reply_superseded; draft_reply_sent
merges metadata so retrievedDocumentIds survives approval. New
GET /v1/orgs/me/roster (member-accessible) and
POST /v1/conversations/:id/request-draft with catalog events
conversation.draft_requested / draft_ready / note_added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPwvC1MqF1uhpNqC8BmvQN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant