feat(GRO-1208): conversations API route + staffReadAt migration #399
Reference in New Issue
Block a user
Delete Branch "feat/GRO-106-staff-messages"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
staffReadAtcolumn toconversationstable for tracking staff read state0032_staff_read_at.sql/api/conversationsHono router with 3 endpoints:GET /— list conversations with unread count (cursor-paginated)GET /:id/messages— paginated messages + mark as read (staffReadAt = NOW())POST /:id/messages— send via outbound service, 409 on opted-out clientTest plan
npx tsc --noEmit)conversations.test.ts🤖 Generated with Claude Code
QA Review — Changes Requested
❌ CI: Lint & Typecheck FAILING
Two unused-import errors are breaking the lint step:
apps/api/src/routes/conversations.tsline 15isNullis imported from@groombook/dbbut never called in the route file. Remove it from the import statement.apps/api/src/__tests__/conversations.test.tsline 181import type { AppEnv } from "../middleware/rbac.js"is present in the test file butAppEnvis not referenced anywhere in the tests. Remove this import.❌ Missing UAT_PLAYBOOK.md update
This PR introduces three new user-facing API endpoints (list conversations, list messages, send message). No
UAT_PLAYBOOK.mdexists in this repo yet.Required: Add or update
UAT_PLAYBOOK.mdwith test cases covering at minimum:✅ What looks good
0032_staff_read_at.sqlis correctclients.tsGET /:id/messagesandPOST /:id/messagestsc --noEmitpassesFix the two lint errors and add
UAT_PLAYBOOK.mdtest cases, then re-request review.QA Re-review — Changes Requested (E2E migration failure)
❌ E2E Tests failing — broken migration journal
The migrate container exits with code 1 during
docker compose up --wait. Root cause:packages/db/migrations/meta/_journal.jsoncontains an entry for0031_steady_veda(idx 31) but no0031_steady_veda.sqlfile exists inpackages/db/migrations/. The Drizzle migration runner tries to load this file and fails.Current journal state on branch:
Required fix — update
packages/db/migrations/meta/_journal.json:0031_steady_vedaentry entirely0032_staff_read_atfrom"idx": 32→"idx": 31Result:
The migration file
0032_staff_read_at.sqlitself is fine and does not need to be renamed.✅ Lint & Typecheck: PASSING (lint fixes confirmed)
✅ Tests: PASSING (11/11)
✅ Build: PASSING
On UAT_PLAYBOOK.md: I accept the clarification that no playbook file exists in this repo and the
promote-to-uat.ymlworkflow has no gating requirement. The UAT_PLAYBOOK.md requirement is waived for this PR — the 5 acceptance criteria in the test file are sufficient.Fix the journal entry and re-request review.
QA Review — Approved ✅
All blocking issues resolved. Full CI green on commit
2213585.Final CI status
Acceptance criteria
0032_staff_read_at.sql+ journal at idx 31businessSettingstsc --noEmitReady to merge to
dev.Deployed to groombook-dev
Images:
pr-399URL: https://dev.groombook.farh.net
Ready for UAT validation.
CTO Review — Approved ✅
Architecture: Clean Hono router, follows established
clients.tspatterns. Business scoping viabusinessSettingsis consistent with single-tenant-per-DB model.Security: Auth guard on all endpoints, cross-tenant 404 on :id routes, Zod validation on POST body (1–1600 chars), Drizzle parameterized queries — no injection surface.
Correctness: Cursor pagination is correct (limit+1 / pop pattern).
staffReadAtmark-as-read side-effect in GET is standard messaging UX. 409 opt-out, 404 cross-tenant, 401 unauthed all verified.Tests: 11 tests covering all 5 acceptance criteria + edge cases (empty body, over-length, cross-tenant, unauthed).
Note for follow-up: N+1 on
GET /conversations(2 subqueries per row) is fine for staff MVP but should be optimized with a subquery/join if conversation counts grow.Ready to merge.