docs(19-01): complete Chat core plan

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-17 20:59:29 +02:00
parent 9404668287
commit fd8321d88f

View File

@@ -0,0 +1,178 @@
---
phase: 19-chat-and-chatchannels
plan: 01
subsystem: chat
tags: [chat, lighthouse, festival, conga, postgres, gorm, testcontainers]
# Dependency graph
requires:
- phase: 18.1
provides: analog sibling-plugin layout (boot steps, IF NOT EXISTS baseline, build-time registry, test-only acme host)
- phase: 11
provides: lighthouse Bind/Emit, MemoryDriver, conga Job on broadcasts
provides:
- "new repository sm-chat-plugin (module git.golem15.com/golem15/sm-chat-plugin, package chat, plugin golem15.chat), pushed to git.golem15.com at b93cae7"
- "idempotent baseline 202610160001_create_golem15_chat_baseline (v1.3.0 tables plus golem15_chat_thread_mutes; D-05 owner column omitted and never dropped)"
- "contracts.Registry / Context (CanActFor, BroadcastChannel); classes.Service ChatService successor; festival MessageCreated/Updated/Deleted"
- "lighthouse Bind alias chat.message (created only, ids-only payload) and chat namespace authorizer (prs)"
- "thread-summary job golem15.chat.maintain_thread_summary and command chat:rewrite-context-types"
affects: [19-02, 19-03, 19-05, sm-chatchannels-plugin, sm-user-plugin]
# Actuals (#2632) - chars/4 over the new plugin tree
actuals:
tokens: 35568
tasks: 3
commits: 3
plan_head_before: 9404668287960f9c128ebc89ad9eca4102c74620
plan_head_after: pending-summary-commit
commits: 3
tech-stack:
added: []
patterns:
- "lighthouse Bind Channels must query through a fresh GORM session (inSavepoint clones the write's SELECT list)"
- "Build-time chat context registry: hosts Add(alias, Loader, LegacyPHPClass); Resolver never skips"
- "Bind created.chat.message is ids-only; full MessageAPI travels on chat:message and REST only"
key-files:
created:
- ../sm-chat-plugin/plugin.go
- ../sm-chat-plugin/boot.go
- ../sm-chat-plugin/contracts/context.go
- ../sm-chat-plugin/contracts/registry.go
- ../sm-chat-plugin/models/conversation.go
- ../sm-chat-plugin/models/message.go
- ../sm-chat-plugin/models/participant.go
- ../sm-chat-plugin/models/thread_summary.go
- ../sm-chat-plugin/models/thread_mute.go
- ../sm-chat-plugin/events/events.go
- ../sm-chat-plugin/classes/service.go
- ../sm-chat-plugin/classes/authorizer.go
- ../sm-chat-plugin/classes/emit.go
- ../sm-chat-plugin/classes/serialize.go
- ../sm-chat-plugin/jobs/maintain_thread_summary.go
- ../sm-chat-plugin/console/rewrite_context_types.go
- ../sm-chat-plugin/updates/202610160001_create_golem15_chat_baseline.go
- ../sm-chat-plugin/tracer_test.go
- ../sm-chat-plugin/README.md
modified: []
key-decisions:
- "Task 1 reply verbatim: proceed-and-push"
- "Locked D-01 names, D-04 id 202610160001_create_golem15_chat_baseline, D-12 rewrite-as-both (service UPDATE + chat:rewrite-context-types)"
- "ROADMAP Phase 19 Repos and SC-19-3 already carried CONTEXT wording (D-15 / Wavepath goldens); no ROADMAP patch"
- "tea repos create --owner golem15 --name sm-chat-plugin succeeded; main pushed at b93cae7 (never force). summercms.go was not pushed."
patterns-established:
- "chat proof host: golem15.user + golem15.chat + test-only acme.teams (Context loader, BroadcastChannel teams:{id})"
requirements-completed: [SC-19-1, SC-19-2, D-01, D-02, D-03, D-04, D-05, D-11, D-12, D-13, D-16, D-19]
coverage:
- id: D1
description: "golem15.chat sibling plugin with the v1.3.0 IF NOT EXISTS baseline (no D-05 owner column) and Chat/Czat display names"
requirement: D-01
verification:
- kind: unit
ref: "go -C ../sm-chat-plugin test -count=1 -short ./..."
status: pass
human_judgment: false
- id: D2
description: "SendMessage persists a text row and MemoryDriver sees created.chat.message ids-only on chat:conversation:{id}"
requirement: D-16
verification:
- kind: integration
ref: "go -C ../sm-chat-plugin test -count=1 -run '^TestChatTracer$' -v ."
status: pass
human_judgment: false
- id: D3
description: "authorizeChat splits locally after presence: strip; three segments; capabilities prs; does not use ChannelID"
requirement: D-16
verification:
- kind: unit
ref: "git.golem15.com/golem15/sm-chat-plugin#TestChatAuthorizer"
status: pass
human_judgment: false
- id: D4
description: "ChatService GetOrCreateDm, MuteThread, Edit/Delete Fire, maintain_thread_summary job, chat:rewrite-context-types"
requirement: D-13
verification:
- kind: other
ref: "acceptance greps on classes/service.go, jobs/maintain_thread_summary.go, console/rewrite_context_types.go"
status: pass
human_judgment: false
duration: 80min
completed: 2026-10-17
status: complete
---
# Phase 19: Chat and ChatChannels — Plan 01 Summary
**sm-chat-plugin exists as a compiled golem15.chat sibling: one SendMessage writes Postgres and MemoryDriver sees an ids-only `created.chat.message`.**
## Performance
- **Duration:** 80 min
- **Started:** 2026-10-17T18:41:16Z
- **Completed:** 2026-10-17T18:58:53Z
- **Tasks:** 3
- **Files created:** 28 in sm-chat-plugin (task 2) then 6 more (task 3)
## Accomplishments
- New module `git.golem15.com/golem15/sm-chat-plugin`, package `chat`, PluginID `golem15.chat`, Requires `golem15.user`.
- Baseline `202610160001_create_golem15_chat_baseline`: conversations, participants, messages, thread_summaries, thread_mutes; every statement `IF NOT EXISTS`; D-05 owner column omitted.
- Context registry with `CanActFor` / `DefaultCanActFor`; chat registers no host aliases.
- `classes.Service` SendMessage plus the PHP method set minus the two D-02 helpers; Edit/Delete Fire `MessageUpdated` / `MessageDeleted`.
- Bind alias `chat.message` (created only, ids-only). Emit helpers for typing, `chat:message`, `chat:read`, `chat:unread`, `deleted.chat.message`.
- Job `golem15.chat.maintain_thread_summary` on `broadcasts`, MaxAttempts 1. Command `chat:rewrite-context-types`.
- Remote `https://git.golem15.com/golem15/sm-chat-plugin` created with tea; `main` pushed at `b93cae7`.
## Task Commits
| Task | Repo | Hash | Message |
|------|------|------|---------|
| 1 | — | — | checkpoint; reply `proceed-and-push` (no files) |
| 2 | sm-chat-plugin | `d6cd1b7563ec4238a630e20105db28fb2f5b65f1` | feat(19-01): task 2 tracer: chat plugin, v1.3.0 baseline, SendMessage and created.chat.message |
| 3 | sm-chat-plugin | `b93cae72227cbd72b76efe31cf0d862302f20607` | feat(19-01): task 3 ChatService writes, Emit helpers, thread-summary job and D-12 rewrite |
## Task 1 reply
`proceed-and-push`
## TestChatTracer
pass (Docker available)
## tea / push
succeeded — `tea repos create --owner golem15 --name sm-chat-plugin`; `git push -u origin main` (never force). summercms.go was not pushed.
## Deviations from Plan
1. **Bind Channels empty on first tracer run (Rule 1).** `lighthouse.inSavepoint` clones the Message write's statement; a `Take` of Conversation on that handle kept the Message SELECT list, so `context_type` / `context_id` stayed nil and Channels returned empty (Pitfall 6). Fixed with a fresh session and an explicit column list (same idea as fonoteka `AlbumChannels`). Auto-fixed.
2. **ROADMAP two-line patch skipped.** Phase 19 Repos already names D-15; SC-19-3 already has the Wavepath golden / `/_wave` wording. No patch.
**Total deviations:** 2 auto-fixed / skipped. **Impact:** none on locked identifiers or the remote.
## Verification
```
go -C ../sm-chat-plugin vet ./... # pass
go -C ../sm-chat-plugin test -count=1 -short ./... # pass
go -C ../sm-chat-plugin test -count=1 -run '^TestChatTracer$' -v . # PASS
```
Acceptance greps (Task 2 and Task 3): all pass. `family_id` count 0. `getFamilyGroupChat` count 0.
## Issues Encountered
None.
## Next Phase Readiness
Plan 19-02 can mount `/_chat/api/v1` on `classes.Service`. Hosts register contexts via `*contracts.Registry`. Wavepath `/_wave` route parity stays out (SC-19-3).
## Self-Check: PASSED