docs(19-06): complete participant-only send and GDPR reassignment plan
Record CR-01/CR-02 gap closure and mark the 19-06 ROADMAP checkbox. Plugin repos were committed separately and not pushed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1198,7 +1198,7 @@ Plans:
|
||||
3. Golden tests show that the serializers (`Message.toApiArray` with attachments, thread summaries, channel, category, pref, pin and mention arrays) and every realtime event (name, channel and payload) match PHP output captured from Wavepath. Route-level parity of `/_wave` is checked at the Wavepath port.
|
||||
4. The new code has unit tests, delivered in the phase's last plan.
|
||||
|
||||
**Plans:** 5/7 plans executed
|
||||
**Plans:** 6/7 plans executed
|
||||
|
||||
Plans:
|
||||
**Wave 1**
|
||||
@@ -1215,7 +1215,7 @@ Plans:
|
||||
- [x] 19-05-PLAN.md — Unit tests LAST: goldens, PHP method map (59 chat + 106 chatchannels), scripts/check-phase19.sh
|
||||
|
||||
**Wave 5** *(gap closure)*
|
||||
- [ ] 19-06-PLAN.md — Participant-only canSend (CR-01) and created_by reassignment on user.Deleting (CR-02)
|
||||
- [x] 19-06-PLAN.md — Participant-only canSend (CR-01) and created_by reassignment on user.Deleting (CR-02)
|
||||
|
||||
**Wave 6** *(gap closure, tests last)*
|
||||
- [ ] 19-07-PLAN.md — TestTeammateDMWriteIDOR, TestDeletingCascade creator path, check-phase19.sh names
|
||||
|
||||
166
.planning/phases/19-chat-and-chatchannels/19-06-SUMMARY.md
Normal file
166
.planning/phases/19-chat-and-chatchannels/19-06-SUMMARY.md
Normal file
@@ -0,0 +1,166 @@
|
||||
---
|
||||
phase: 19-chat-and-chatchannels
|
||||
plan: 06
|
||||
subsystem: chat
|
||||
tags: [chat, chatchannels, canSend, GDPR, user.Deleting, D-08, D-13, SC-19-1]
|
||||
|
||||
requires:
|
||||
- phase: 19-02
|
||||
provides: ChatService SendMessage, GetOrCreateDm, GetGroupChat
|
||||
- phase: 19-04
|
||||
provides: user.Deleting, FireDeletingEvent, ChannelService.WithDB analog
|
||||
provides:
|
||||
- "CR-01 / D-08 / SC-19-1: canSend is participant-only; teammate DM write is ErrNotParticipant"
|
||||
- "CR-02 / D-13: chat-owned ReassignCreatedBy on user.Deleting so remaining members keep the conversation"
|
||||
- "TestGapClosureSmoke (postgres, skipped under -short)"
|
||||
affects: [19-07, Wavepath port]
|
||||
|
||||
actuals:
|
||||
tokens: 3037
|
||||
tasks: 3
|
||||
commits: 1
|
||||
plan_head_before: 29d06bc70e0e1e4f0d24dcb72a3dd304863994cb
|
||||
plan_head_after: pending-summary-commit
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "canSend returns s.isParticipant after nil/zero-id guards; host ChatEnabled stays on GetGroupChat / GetOrCreateDm"
|
||||
- "Service.WithDB clones like ChannelService.WithDB so the deleting event Tx is the session"
|
||||
- "Chat owns user.Deleting reassignment so DMs survive when chatchannels is not mounted"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../sm-chat-plugin/gap_closure_test.go
|
||||
modified:
|
||||
- ../sm-chat-plugin/classes/service.go
|
||||
- ../sm-chat-plugin/boot.go
|
||||
- ../sm-chat-plugin/README.md
|
||||
- ../sm-chatchannels-plugin/README.md
|
||||
- .planning/ROADMAP.md
|
||||
|
||||
key-decisions:
|
||||
- "Chat owns ReassignCreatedBy; chatchannels SeverUserFromContext is unchanged"
|
||||
- "canSend is participation-only; ChatEnabled is not folded into canSend"
|
||||
- "GetGroupChat still ignores addParticipant errors (IN-01 stays open)"
|
||||
- "TestGapClosureSmoke is not added to check-phase19.sh (19-07)"
|
||||
|
||||
patterns-established:
|
||||
- "Gap-closure smoke lives in the plugin repo and is skipped under -short via TestMain"
|
||||
- "GDPR created_by reassignment is chat-owned so DMs and channel-backed groups both survive"
|
||||
|
||||
requirements-completed: [SC-19-1, D-08, D-13]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "canSend is participant-only; a third teammate cannot SendMessage into a two-person DM (CR-01 / D-08 / SC-19-1)"
|
||||
requirement: SC-19-1
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../sm-chat-plugin/gap_closure_test.go#TestGapClosureSmoke"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "user.Deleting reassigns created_by to another remaining participant so the DM row survives GDPR delete (CR-02 / D-13)"
|
||||
requirement: D-13
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../sm-chat-plugin/gap_closure_test.go#TestGapClosureSmoke"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "READMEs document participant-only send and created_by reassignment; WR-01..WR-04 and IN-01..IN-02 stay open"
|
||||
requirement: D-08
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "grep ReassignCreatedBy/participant/created_by in plugin READMEs"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: 4min
|
||||
completed: 2026-10-17
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 19 Plan 06: Chat/ChatChannels Gap Closure Summary
|
||||
|
||||
**Participant-only canSend and chat-owned created_by reassignment on user.Deleting so DMs and shared channels survive GDPR delete**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 4 min
|
||||
- **Started:** 2026-10-17T20:28:09Z
|
||||
- **Completed:** 2026-10-17T20:32:00Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 5
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `canSend` now returns only `s.isParticipant` after nil/zero-id guards. A teammate who is not a DM participant gets `ErrNotParticipant` (HTTP 404 mapping unchanged).
|
||||
- `Service.WithDB` and `Service.ReassignCreatedBy` land; Boot Listens `user.Deleting` and reassigns `created_by` to another remaining participant before the user row is deleted. The IF NOT EXISTS baseline CASCADE is untouched.
|
||||
- `TestGapClosureSmoke` proves both post-fix behaviours on one postgres path (Carol refused on Alice/Bob DM; Alice send succeeds; Carol GetGroupChat lazy-join then send succeeds; after Alice delete the DM remains with `created_by` = Bob).
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically in the plugin repos; this SUMMARY is the summercms.go metadata commit.
|
||||
|
||||
1. **Task 1 + 2 + 3 (sm-chat-plugin):** `0aee785` (feat) — participant-only send, ReassignCreatedBy, TestGapClosureSmoke, README
|
||||
2. **Task 2 + 3 (sm-chatchannels-plugin):** `7d49c3c` (docs) — GDPR bullet that chat reassigns created_by
|
||||
3. **Plan metadata (summercms.go):** this commit (docs)
|
||||
|
||||
_Plugin SHAs are local only. No remotes were pushed. fonoteka submodule pointers were not bumped._
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `../sm-chat-plugin/classes/service.go` — participant-only `canSend`; `WithDB`; `ReassignCreatedBy`
|
||||
- `../sm-chat-plugin/boot.go` — `listeners` step; `Listen[*userclasses.DeletingEvent]`
|
||||
- `../sm-chat-plugin/gap_closure_test.go` — `TestGapClosureSmoke`
|
||||
- `../sm-chat-plugin/README.md` — send-rights, GetGroupChat lazy-join, user.Deleting / ReassignCreatedBy
|
||||
- `../sm-chatchannels-plugin/README.md` — remaining members keep the channel because chat reassigns created_by
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Chat owns `user.Deleting` reassignment so DMs survive even when chatchannels is not mounted. chatchannels `SeverUserFromContext` still only removes that user's members/prefs/participants/personal categories.
|
||||
- `canSend` is participation-only. Host `ChatEnabled` stays on `GetGroupChat` and `GetOrCreateDm`.
|
||||
- `GetGroupChat` keeps `_ = addParticipant(...)` (IN-01 stays open).
|
||||
- Named postgres tests `TestTeammateDMWriteIDOR` / `TestDeletingCascade` creator-delete assertions and `scripts/check-phase19.sh` list updates wait for 19-07.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written.
|
||||
|
||||
**Total deviations:** 0 auto-fixed
|
||||
**Impact on plan:** None.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `go -C ../sm-chat-plugin vet ./...` — PASS
|
||||
- `go -C ../sm-chat-plugin test -count=1 -short ./...` — PASS (Task 1 and Task 2)
|
||||
- `go -C ../sm-chatchannels-plugin test -count=1 -short ./...` — PASS
|
||||
- `go -C ../sm-chat-plugin test -count=1 -run '^TestGapClosureSmoke$' -v .` — PASS (`--- PASS: TestGapClosureSmoke (0.72s)`)
|
||||
- Baseline `golem15_chat_conversations_created_by_foreign` / `ON DELETE CASCADE` unchanged
|
||||
- WR-01..WR-04 and IN-01..IN-02 confirmed unread: GetGroupChat still ignores addParticipant errors; download Content-Disposition unchanged; JoinChannel / PinMessage unchanged; FormAfterDelete remains the only production `FireDeletingEvent` site
|
||||
- No remotes pushed (`sm-chat-plugin` and `sm-chatchannels-plugin` are `ahead 1`)
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
Ready for 19-07: `TestTeammateDMWriteIDOR`, `TestDeletingCascade` creator-delete assertions, and `scripts/check-phase19.sh` required-name updates. Production CR-01 and CR-02 are closed.
|
||||
|
||||
---
|
||||
*Phase: 19-chat-and-chatchannels*
|
||||
*Completed: 2026-10-17*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: `.planning/phases/19-chat-and-chatchannels/19-06-SUMMARY.md`
|
||||
- FOUND: `../sm-chat-plugin/gap_closure_test.go`
|
||||
- FOUND: `../sm-chat-plugin` commit `0aee785`
|
||||
- FOUND: `../sm-chatchannels-plugin` commit `7d49c3c`
|
||||
Reference in New Issue
Block a user