Skip to content

feat(store,api): four-state conversation status — active/pending/closed/spam (HT-26) - #25

Merged
zaridan merged 1 commit into
mainfrom
feat/ht-26-status-model
Jul 12, 2026
Merged

feat(store,api): four-state conversation status — active/pending/closed/spam (HT-26)#25
zaridan merged 1 commit into
mainfrom
feat/ht-26-status-model

Conversation

@zaridan

@zaridan zaridan commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Why

First implementation increment of the v1.1 contract (HT-26; spec amendment HT-25 / #24). The status model is the foundational change every other v1.1 ticket layers on.

What

Migration 004ConversationStatus becomes active | pending | closed | spam (+deleted, unchanged and never surfaced):

  • Old CHECK dropped before the open → active backfill (updating first would fail on any non-fresh DB — same discipline as migration 002's backfill), new CHECK added after.
  • Column default moves to 'active', so the inbound-mail path's status-less INSERT is untouched.
  • 'open' is no longer a legal value — a rename, not a widening.

Store

  • New ConversationStatus / ConversationFolder types.
  • listConversations now filters by folder (open = active + pending; closed/spam exact; omitted = everything but deleted). Summaries carry the real status.
  • setConversationStatus accepts all four; deleted remains unreachable (and now unsettable by type).
  • appendThread reopens closed and spam to active; pending deliberately stays pending — it's an Agent statement the spec says is never set (or cleared) automatically.

API

  • GET /conversations?status= accepts open|closed|spam; raw statuses (active/pending) are rejected 400 — folders are the reading grain.
  • PATCH accepts all four statuses; deleted still 400.

Evidence

  • npm test: 341/341 pass (18 files), including 10 new tests: migration-004 upgrade path over a seeded pre-004 database ('open'→'active', closed/deleted untouched), fresh-install CHECK/default behavior, spam-reopen + pending-stays-pending at store and API level, folder semantics incl. the active+pending union, PATCH pending/spam round-trips, and raw-status filter rejection.
  • Typecheck + Biome clean.
  • Mail semantics untouched: no change to parse/thread/send paths — the reopen policy edit is the store's append policy (already spec'd), verified by the existing round-trip send test still passing.

Spec

Written against specs/api/agent-inbox-v1.md v1.1 as amended in #24 (§2 status semantics, §3a folder listing, §4a spam-reopen, §4b four-value PATCH). Merge #24 first (or together) so spec and behavior land consistently.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Expanded conversation statuses to active, pending, closed, and spam.
    • Added folder-based conversation filtering, with the open folder including active and pending conversations.
    • Added support for setting pending and spam statuses.
    • Replies now reopen closed or spam conversations to active.
  • Bug Fixes

    • Existing open conversations are migrated to active without affecting closed or deleted conversations.

…ed/spam (HT-26)

Migration 004 renames 'open' to 'active' (old CHECK dropped before the
backfill — order is load-bearing) and adds 'pending' and 'spam'; the
column default moves to 'active' so inbound mail keeps creating
conversations through the unchanged status-less INSERT.

Store: ConversationStatus/ConversationFolder types; listConversations
filters by FOLDER (open = active + pending; closed/spam exact);
setConversationStatus accepts all four (deleted still excluded);
appendThread reopens closed AND spam to active — pending deliberately
stays pending (an Agent statement, never auto-cleared).

API: GET ?status= accepts open|closed|spam folders (active/pending
rejected — folders are the reading grain); PATCH accepts all four
statuses; wire summaries carry the real status.

Per specs/api/agent-inbox-v1.md §2/§3a/§4a/§4b as amended to v1.1
(HT-25, PR #24). Upgrade path proven: migration-004-over-003 test
seeds 'open' rows and verifies the backfill; the full suite runs
against both PGlite and the Postgres adapter schema test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR migrates conversations from open/closed semantics to active, pending, closed, and spam statuses, while retaining deleted storage rows. It adds database migration 004, folder-based listing, expanded status updates, reopening behavior, and corresponding API, store, migration, and integration tests.

Changes

Conversation status model

Layer / File(s) Summary
Database status migration
src/db/migrate.ts, src/db/migrate.test.ts, src/db/postgres.test.ts
Migration 004 backfills open to active, changes the default, and enforces active, pending, closed, spam, or deleted.
Store status and folder behavior
src/store/conversations.ts, src/store/conversations.test.ts, src/dev/seed.test.ts
Store types, reopening rules, status updates, and folder filters support the four-state model; open includes active and pending.
API status and folder integration
src/api/conversations.ts, src/api/index.test.ts
API validation, summaries, reply reopening, patch status handling, and endpoint tests use the expanded statuses and folder queries.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant conversations_api
  participant ConversationStore
  Client->>conversations_api: list with status folder
  conversations_api->>ConversationStore: listConversations with folder
  ConversationStore-->>conversations_api: summaries with ConversationStatus
  conversations_api-->>Client: conversation summaries
  Client->>conversations_api: patch status or reply
  conversations_api->>ConversationStore: update or append thread
  ConversationStore-->>conversations_api: updated conversation status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding four conversation states across store and API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ht-26-status-model

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/store/conversations.ts (1)

383-384: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale doc reference: options.statusoptions.folder.

This PR renames ListConversationsOptions.status to folder (see lines 465-471), but this doc comment on listConversations still says options.status and links to {@link ListConversationsOptions.status}, which no longer exists. The @link is now broken and the prose is misleading for future readers.

📝 Proposed fix
-   * `deleted` conversation is NEVER returned, regardless of
-   * `options.status` — see {`@link` ListConversationsOptions.status}.
+   * `deleted` conversation is NEVER returned, regardless of
+   * `options.folder` — see {`@link` ListConversationsOptions.folder}.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/store/conversations.ts` around lines 383 - 384, Update the
listConversations documentation to refer to options.folder and link to
ListConversationsOptions.folder instead of the removed status property,
preserving the existing statement that deleted conversations are never returned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/store/conversations.ts`:
- Around line 383-384: Update the listConversations documentation to refer to
options.folder and link to ListConversationsOptions.folder instead of the
removed status property, preserving the existing statement that deleted
conversations are never returned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9dd3fde6-1b36-4cdb-89a5-a3477418d1d7

📥 Commits

Reviewing files that changed from the base of the PR and between 5a23c33 and fefc86a.

📒 Files selected for processing (8)
  • src/api/conversations.ts
  • src/api/index.test.ts
  • src/db/migrate.test.ts
  • src/db/migrate.ts
  • src/db/postgres.test.ts
  • src/dev/seed.test.ts
  • src/store/conversations.test.ts
  • src/store/conversations.ts

@zaridan
zaridan merged commit 127af9d into main Jul 12, 2026
5 checks passed
@zaridan
zaridan deleted the feat/ht-26-status-model branch August 2, 2026 19:19
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