feat(store,api): internal notes — direction 'note' + POST /conversations/{id}/notes (HT-28) - #29
Merged
Merged
Conversation
…versations/{id}/notes (HT-28)
A note is Agent-only context on a conversation: never emailed, no
token minted, no outbox row, invisible to the delivery worker.
Migration 007 swaps two constraints together (they must ship as a
pair): the direction CHECK widens to admit 'note', and the
delivery-status-by-direction CHECK requires notes to carry NULL like
inbound — the old constraint would have rejected every note row.
Store: appendThread's reopen policy is note-aware — a note on a
closed/spam conversation bumps updated_at (it IS activity) but never
reopens (it is not the customer coming back). The mail boundary is
test-asserted: an aged note is invisible to listDeliverableThreads
even at staleAfterMs 0, and the API test proves the sender is never
invoked (charter invariant #5 adjacency, spec §4c).
API: POST /conversations/{id}/notes — {text} 1-5000, plain text in
v1; 201 with the note ThreadView (direction 'note', from = support
address, deliveryStatus null).
377/377 tests. Per specs/api/agent-inbox-v1.md §4c (v1.1, HT-25).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughAdds internal note threads with database constraints, store behavior, and a new authenticated ChangesInternal note thread persistence
Notes API
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant matchRoute
participant handlePostNote
participant ConversationStore
Client->>matchRoute: POST /api/v1/conversations/{id}/notes
matchRoute-->>handlePostNote: conversation-note route and id
handlePostNote->>ConversationStore: appendThread(direction: note)
ConversationStore-->>handlePostNote: created note thread
handlePostNote-->>Client: 201 thread view
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Fifth v1.1 increment (HT-28) — internal notes, the warn-tinted bands in the designed UI. Spec:
agent-inbox-v1.md§4c (v1.1, #24).What
Migration 007 — two constraint swaps that must ship as a pair: the direction CHECK widens to admit
'note', andthreads_delivery_status_by_directiongets a note arm requiring NULL (the old constraint satisfied neither arm for a note and would have rejected every note row). No backfill; pre-007 rules verified intact after the swap.Store —
appendThreadis now note-aware: a note bumpsupdatedAt(it's activity; the conversation resurfaces) but never reopens a closed/spam conversation (it is not the customer coming back).directionunions widen acrossNewThread/StoredThread.API —
POST /conversations/{id}/notes, body{ text }(1–5000, plain text in v1) → 201 with the noteThreadView(direction: 'note',from= support address,deliveryStatus: null). Generic 404 for missing/deleted/non-UUID; 405Allow: POST; 401.The mail boundary (charter invariant #5 adjacency) — spec §4c says a note reaching the send path is a bug, so it's test-asserted from both sides: the handler never touches
sendReply(the fake sender records zero sends), and an aged note is invisible tolistDeliverableThreadseven atstaleAfterMs: 0.Evidence
appendThread, covered by the existing reply-reopen tests plus the new note-never-reopens tests.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Validation
Data Integrity