docs(19-02): complete Chat HTTP API plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
235
.planning/phases/19-chat-and-chatchannels/19-02-SUMMARY.md
Normal file
235
.planning/phases/19-chat-and-chatchannels/19-02-SUMMARY.md
Normal file
@@ -0,0 +1,235 @@
|
||||
---
|
||||
phase: 19-chat-and-chatchannels
|
||||
plan: 02
|
||||
subsystem: chat-api
|
||||
tags: [chat, jwt, attachments, throttle, lighthouse]
|
||||
|
||||
requires:
|
||||
- phase: 19-01
|
||||
provides: classes.Service SendMessage, GetOrCreateDm, MuteThread, Emit helpers, acme.teams proof host
|
||||
provides:
|
||||
- "Published /_chat/api/v1 with jwt.auth, chat-api (120/min) and chat-dm (10/min)"
|
||||
- "Context addressing /contexts/{type}/{id}/conversation and /dm (D-08)"
|
||||
- "D-07 PUT /threads/{rootId}/mute on golem15_chat_thread_mutes"
|
||||
- "Private attach.Store uploads and participation-gated download (D-18)"
|
||||
affects: [19-03, 19-05, Wavepath port]
|
||||
|
||||
actuals:
|
||||
tokens: 21563
|
||||
tasks: 3
|
||||
commits: 0
|
||||
plan_head_before: a2c7366b2a7f6e898eac57d34b5516cc3f988fbc
|
||||
plan_head_after: a2c7366b2a7f6e898eac57d34b5516cc3f988fbc
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Handlers call classes.Service; they do not write rows"
|
||||
- "jwt.auth before throttle so named buckets key by frontend user id"
|
||||
- "IDOR collapses to 404; DM refusal is 422 CANNOT_CREATE_DM"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../sm-chat-plugin/routes.go
|
||||
- ../sm-chat-plugin/api/auth.go
|
||||
- ../sm-chat-plugin/api/conversations.go
|
||||
- ../sm-chat-plugin/api/messages.go
|
||||
- ../sm-chat-plugin/api/dm.go
|
||||
- ../sm-chat-plugin/api/replies.go
|
||||
- ../sm-chat-plugin/api/read.go
|
||||
- ../sm-chat-plugin/api/typing.go
|
||||
- ../sm-chat-plugin/api/mute.go
|
||||
- ../sm-chat-plugin/api/threads.go
|
||||
- ../sm-chat-plugin/api/attachments.go
|
||||
- ../sm-chat-plugin/api/download.go
|
||||
- ../sm-chat-plugin/classes/attachment_policy.go
|
||||
- ../sm-chat-plugin/http_tracer_test.go
|
||||
modified:
|
||||
- ../sm-chat-plugin/plugin.go
|
||||
- ../sm-chat-plugin/boot.go
|
||||
- ../sm-chat-plugin/classes/service.go
|
||||
- ../sm-chat-plugin/classes/serialize.go
|
||||
- ../sm-chat-plugin/README.md
|
||||
|
||||
key-decisions:
|
||||
- "Task 1 reply verbatim: proceed"
|
||||
- "Locked APIPrefix /_chat/api/v1 and the RESEARCH path list including PUT /threads/{rootId}/mute"
|
||||
- "jwt.auth runs before throttle so chat-api and chat-dm key by frontend user id else IP"
|
||||
- "SendMessage claims chat-pending-{actorID} rows; another actor's pending upload is refused"
|
||||
- "Did not Emit updated.chat.message; Q4 defers that to the plan 05 Wavepath capture"
|
||||
|
||||
patterns-established:
|
||||
- "Generic chat routes live under /_chat/api/v1 with jwt.auth; friendship DMs stay on the Wavepath port"
|
||||
- "Attachment URLs are /_chat/api/v1/attachments/{messageId}/{fileId}; downloads 404 for stranger and missing"
|
||||
|
||||
requirements-completed: [SC-19-1, SC-19-3, D-06, D-07, D-08, D-09, D-10, D-18]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "Published /_chat/api/v1 prefix with jwt.auth and named buckets"
|
||||
requirement: D-06
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -short ./...#TestChatHTTPAuth"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "grep const APIPrefix and jwt.auth in routes.go"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Authenticated context member can POST one message; stranger and missing id are 404"
|
||||
requirement: D-08
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -run '^TestChatHTTPTracer$' -v .#TestChatHTTPTracer"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "PUT /threads/{rootId}/mute writes chat-owned golem15_chat_thread_mutes with root_id/is_muted"
|
||||
requirement: D-07
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -run '^TestThreadMute$' -v .#TestThreadMute"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "chat-dm throttle and 422 CANNOT_CREATE_DM; no legacy /api/chat routes"
|
||||
requirement: D-09
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -run '^TestDMThrottle$' -v .#TestDMThrottle"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "Private attachments with ChatAttachmentPolicy limits and participation-gated download"
|
||||
requirement: D-18
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -short ./...#TestAttachmentPolicy"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "go -C ../sm-chat-plugin test -count=1 -run '^TestAttachmentDownload$' -v .#TestAttachmentDownload"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: 7min
|
||||
completed: 2026-10-17
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 19 Plan 02: Chat HTTP API Summary
|
||||
|
||||
**Published `/_chat/api/v1` with jwt.auth, named buckets, context-scoped DMs, chat-owned thread mute, and participation-gated private attachments**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 7 min
|
||||
- **Started:** 2026-10-17T19:15:12Z
|
||||
- **Completed:** 2026-10-17T19:21:50Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 23 (in sm-chat-plugin)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Locked the published prefix and RESEARCH path list (Task 1 reply `proceed`).
|
||||
- An authenticated `acme.teams` member can POST a text message under `/_chat/api/v1`; a stranger JWT and a missing conversation id are 404 (`TestChatHTTPTracer` PASS).
|
||||
- Remaining D-06 / D-07 operations shipped: DM (`chat-dm` 10/min), replies, read, typing, conversation mute, `PUT /threads/{rootId}/mute`, edit/delete, pending upload and private download.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Production commits live in `sm-chat-plugin` (this repo had no production diff):
|
||||
|
||||
1. **Task 1: Confirm the published /_chat/api/v1 contract (D-06)** — checkpoint; human reply `proceed` (no code commit)
|
||||
2. **Task 2: End-to-end POST one message on /_chat/api/v1** — `76ab629` (feat) in sm-chat-plugin
|
||||
3. **Task 3: DMs, replies, read, typing, mute, thread mute, private attachments** — `f9defb9` (feat) in sm-chat-plugin
|
||||
|
||||
**Plan metadata:** pending this SUMMARY commit in summercms.go
|
||||
|
||||
_Note: sm-chat-plugin `main` was pushed to origin at `f9defb9` (includes Task 2). summercms.go was not pushed._
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `../sm-chat-plugin/routes.go` — `APIPrefix` `/_chat/api/v1`, `chat-api` / `chat-dm` buckets, jwt.auth groups
|
||||
- `../sm-chat-plugin/api/*.go` — handlers that call `classes.Service` and write `wire.WriteJSON`
|
||||
- `../sm-chat-plugin/classes/attachment_policy.go` — MaxBytes 102400*1024, KNOWN_EXTS, banned SVG/HTML/XML
|
||||
- `../sm-chat-plugin/classes/attachments.go` — pending store, claim, download lookup
|
||||
- `../sm-chat-plugin/http_tracer_test.go` — TestChatHTTPAuth, TestChatHTTPTracer, TestThreadMute, TestDMThrottle, TestAttachmentPolicy, TestAttachmentDownload
|
||||
- `../sm-chat-plugin/README.md` — published paths and private-download rule
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Task 1 reply verbatim: **proceed**
|
||||
- `jwt.auth` wraps before throttle so the bucket key sees the frontend user (D-09 "per user else IP")
|
||||
- Attachment claiming is inside `SendMessage` (PHP WR-01); pending field is `chat-pending-{actorID}`
|
||||
- `updated.chat.message` is not emitted until the plan 05 Wavepath capture (RESEARCH Q4)
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 2 - Missing Critical] Claim pending attachments inside SendMessage**
|
||||
- **Found during:** Task 3
|
||||
- **Issue:** 19-01 `SendMessage` accepted `attachmentIDs` but never claimed rows, so D-18 uploads could not become message attachments
|
||||
- **Fix:** `claimAttachments` scopes to `field=chat-pending-{sender}` and unattached private files; max 5
|
||||
- **Files modified:** `classes/service.go`, `classes/attachments.go`
|
||||
- **Verification:** TestAttachmentDownload
|
||||
- **Committed in:** `f9defb9`
|
||||
|
||||
**2. [Rule 2 - Missing Critical] jwt.auth before throttle**
|
||||
- **Found during:** Task 3 (chat-dm user key)
|
||||
- **Issue:** `Use("throttle", "jwt.auth")` keys every request by IP because the principal is not set yet
|
||||
- **Fix:** `Use("jwt.auth", "throttle:"+bucket)`
|
||||
- **Files modified:** `routes.go`
|
||||
- **Verification:** TestDMThrottle (11th request 429)
|
||||
- **Committed in:** `f9defb9`
|
||||
|
||||
**3. [Rule 1 - Bug] SendTypingIndicator returned nil for a non-participant**
|
||||
- **Found during:** Task 3
|
||||
- **Issue:** `if err != nil || !ok { return err }` swallowed the unauthorized case
|
||||
- **Fix:** return `ErrNotParticipant` when `!ok`
|
||||
- **Files modified:** `classes/queries.go`
|
||||
- **Verification:** maps to 404 via writeChatError
|
||||
- **Committed in:** `f9defb9`
|
||||
|
||||
**4. [Rule 3 - Blocking] Dropped group-level Where("type")**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** surf refuses a Where constraint that is not a path param of every route in the group
|
||||
- **Fix:** validate `{type}` in the handler (empty / unknown → 404)
|
||||
- **Files modified:** `routes.go`
|
||||
- **Verification:** TestChatHTTPTracer
|
||||
- **Committed in:** `76ab629`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 4 auto-fixed (2 missing critical, 1 bug, 1 blocking)
|
||||
**Impact on plan:** Required for D-09 user-keyed buckets and D-18 claim semantics. No scope creep.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- `TestThreadMute`, `TestDMThrottle` and `TestAttachmentDownload` need Postgres (same harness as TestChatHTTPTracer). They skip under `-short` via `freshDB`. `TestAttachmentPolicy` and `TestChatHTTPAuth` run under `-short`.
|
||||
- Identifiers named `parent` trip `TestFixtureAbsentFromProduction`; reply source rows use `src` / `sourceID`.
|
||||
|
||||
## Authentication Gates
|
||||
|
||||
None.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
Ready for 19-03 (ChatChannels). The Wavepath port can thin-alias `/_chat/api/v1`. Friendship DMs stay off this prefix.
|
||||
|
||||
**TestChatHTTPTracer:** PASS (Docker)
|
||||
**sm-chat-plugin pushed:** yes, `origin/main` at `f9defb9` (never force)
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- routes.go, api/download.go, classes/attachment_policy.go, http_tracer_test.go present
|
||||
- plugin commits `76ab629` and `f9defb9` present
|
||||
- Task 1 reply recorded verbatim as `proceed`
|
||||
|
||||
---
|
||||
*Phase: 19-chat-and-chatchannels*
|
||||
*Completed: 2026-10-17*
|
||||
Reference in New Issue
Block a user