docs(19.1-01): summarize userfriends tracer, groups, adapter and GDPR

Record the proceed-and-push lock, sibling repo commits, tea remote, and Task 3 deviations so later 19.1 plans can consume the new plugin.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-18 13:21:39 +02:00
parent f984e5e63f
commit 03591e1d6c

View File

@@ -0,0 +1,225 @@
---
phase: 19.1-chat-satellites
plan: 01
subsystem: userfriends
tags: [userfriends, friends, groups, festival, postgres, gorm, testcontainers, jwt]
# Dependency graph
requires:
- phase: 19
provides: analog sibling-plugin layout (boot steps, IF NOT EXISTS baseline, build-time chat Registry, test-only proof host)
- phase: 11
provides: festival Bus Listen/Fire, lagoon.Transaction, surf buckets
provides:
- "new repository sm-userfriends-plugin (module git.golem15.com/golem15/sm-userfriends-plugin, package userfriends, plugin golem15.userfriends), pushed to git.golem15.com at 2480ff0"
- "idempotent baseline 202610180001_create_golem15_userfriends_baseline (six v1.1.0 tables, FriendContext singleton id=1, demo-seed v1.0.1 as comment)"
- "FriendshipService + GroupService; festival userfriends.GroupCreated/GroupDeleted/MemberJoined/MemberLeft; optional friendcontext chat adapter; user.Deleting GDPR heir"
- "generic JSON API under /_userfriends/api/v1 with userfriends-api 120/min and community-join 10/min"
affects: [19.1-02, 19.1-06, sm-forum-plugin, sm-chat-plugin]
actuals:
tokens: 40250
tasks: 3
commits: 1
plan_head_before: 36634313afd252c8f7a5becdca420c56603d1ac6
plan_head_after: pending-summary-commit
tech-stack:
added: []
patterns:
- "Soft chat adapter: Lookup[*chatcontracts.Registry] at Boot and skip when absent; Requires() stays golem15.user only"
- "GDPR user.Deleting heir: ORDER BY role ASC, created_at ASC (admin before member); update owner_id so ON DELETE CASCADE does not drop the group"
- "Generic friends API: actor from JWT; IDOR/unauthorized collapse to 404; community-join Max 10 on POST /friends/request and POST /groups/{id}/join"
key-files:
created:
- ../sm-userfriends-plugin/plugin.go
- ../sm-userfriends-plugin/boot.go
- ../sm-userfriends-plugin/routes.go
- ../sm-userfriends-plugin/events/events.go
- ../sm-userfriends-plugin/classes/friendship_service.go
- ../sm-userfriends-plugin/classes/group_service.go
- ../sm-userfriends-plugin/classes/group_policy.go
- ../sm-userfriends-plugin/classes/group_visibility_gate.go
- ../sm-userfriends-plugin/classes/friends_context_adapter.go
- ../sm-userfriends-plugin/api/friends.go
- ../sm-userfriends-plugin/api/groups.go
- ../sm-userfriends-plugin/updates/202610180001_create_golem15_userfriends_baseline.go
- ../sm-userfriends-plugin/http_tracer_test.go
- ../sm-userfriends-plugin/README.md
modified:
- .planning/ROADMAP.md
key-decisions:
- "Task 1 reply verbatim: proceed-and-push"
- "Locked D-01 names (module git.golem15.com/golem15/sm-userfriends-plugin, package userfriends, PluginID golem15.userfriends, Requires golem15.user only), D-02 baseline id 202610180001_create_golem15_userfriends_baseline, D-05 prefix /_userfriends/api/v1"
- "tea repos create --owner golem15 --name sm-userfriends-plugin succeeded; main pushed at 2480ff0 (never force). summercms.go was not pushed."
patterns-established:
- "userfriends proof host: golem15.user + golem15.userfriends + a test-only teams fixture; TestFixtureAbsentFromProduction keeps the fixture id and four retired tokens out of production sources"
requirements-completed: [SC-19.1-1, SC-19.1-3, D-01, D-02, D-03, D-04, D-05, D-07, D-08]
coverage:
- id: D1
description: "Human lock of D-01 plugin names, D-02 baseline id and D-05 prefix before the sibling repo existed"
requirement: D-01
verification: []
human_judgment: true
rationale: "Task 1 was checkpoint:decision gate=blocking-human; the user replied proceed-and-push and that lock cannot be asserted by a unit test"
- id: D2
description: "sm-userfriends-plugin with v1.1.0 IF NOT EXISTS baseline, FriendshipService, and JWT request/accept/list tracer under /_userfriends/api/v1"
requirement: D-05
verification:
- kind: unit
ref: "go -C ../sm-userfriends-plugin test -count=1 -short ./..."
status: pass
- kind: integration
ref: "../sm-userfriends-plugin/http_tracer_test.go#TestHTTPTracer"
status: pass
human_judgment: false
- id: D3
description: "ROADMAP Phase 19.1 Repos names summercms.go; SC2 names webpush-go / LiveKit / bluemonday notes; SC3 marked already met"
requirement: SC-19.1-3
verification:
- kind: other
ref: "grep webpush-go .planning/ROADMAP.md (Task 2 acceptance)"
status: pass
human_judgment: false
- id: D4
description: "GroupService + festival events + optional friendcontext adapter + user.Deleting GDPR heir"
requirement: D-08
verification:
- kind: unit
ref: "go -C ../sm-userfriends-plugin vet ./... && go -C ../sm-userfriends-plugin test -count=1 -short ./..."
status: pass
human_judgment: false
- id: D5
description: "Gitea remote golem15/sm-userfriends-plugin created and main pushed (never force)"
requirement: SC-19.1-1
verification:
- kind: other
ref: "tea repos create --owner golem15 --name sm-userfriends-plugin; git push -u origin main"
status: pass
human_judgment: false
duration: 12min
completed: 2026-10-18
status: complete
---
# Phase 19.1 Plan 01: Userfriends repo, baseline, friends tracer Summary
**Sibling `sm-userfriends-plugin` with the v1.1.0 IF NOT EXISTS baseline, a JWT friends HTTP tracer, group festival events, an optional chat adapter, and a GDPR heir on `user.Deleting`.**
## Performance
- **Duration:** 12 min
- **Started:** 2026-10-18T11:09:20Z
- **Completed:** 2026-10-18T11:21:00Z
- **Tasks:** 3
- **Files modified:** 37 (36 in the sibling plugin, plus `.planning/ROADMAP.md`)
## Task 1 human reply (verbatim)
proceed-and-push
Meaning recorded at resume: lock D-01 plugin names, D-02 baseline id `202610180001_create_golem15_userfriends_baseline`, D-05 prefix `/_userfriends/api/v1`; implement; after Task 3 is green, tea-create the remote and push main (never force).
## Accomplishments
- New independent git repo `../sm-userfriends-plugin` (not a submodule): module `git.golem15.com/golem15/sm-userfriends-plugin`, package `userfriends`, PluginID `golem15.userfriends`, `Requires()` = `golem15.user` only.
- Idempotent baseline `202610180001_create_golem15_userfriends_baseline` for the six PHP v1.1.0 tables; FriendContext singleton id=1; demo-seed v1.0.1 is a comment with no demo INSERTs.
- `FriendshipService` request/accept/list plus remaining CommunityFriendsController paths; generic API prefix `/_userfriends/api/v1`; buckets `userfriends-api` Max 120 and `community-join` Max 10 Decay `time.Minute`.
- `TestHTTPTracer` (Docker): JWT user A requests B, B accepts, A lists friends 200.
- `GroupService` + `GroupPolicy` + `GroupVisibilityGate`; festival `userfriends.GroupCreated` / `GroupDeleted` / `MemberJoined` / `MemberLeft` fire inside the write transaction.
- Soft chat adapter: Boot Lookup of `*chatcontracts.Registry`; skip when absent; Add alias `friendcontext` with LegacyPHPClass `Golem15\UserFriends\Models\FriendContext` (and `friendgroup`).
- GDPR: Listen `user.Deleting`; heir oldest admin else oldest member (`ORDER BY role ASC, created_at ASC`, not MySQL `FIELD()`); else hard-delete empty group; then member and ban rows. `owner_id` is updated to the heir so Postgres `ON DELETE CASCADE` does not drop a transferred group.
- ROADMAP Phase 19.1 Repos adds `summercms.go`; criterion 2 names webpush-go / LiveKit / bluemonday notes and drops AI-plugin wiring; criterion 3 marked already met (frozen PHP userfriends `182b514`).
- Remote created: `https://git.golem15.com/golem15/sm-userfriends-plugin`. `git push -u origin main` succeeded (never force). summercms.go was not pushed.
## Task Commits
Each remaining task was committed atomically. Task 1 wrote no files.
1. **Task 1: Confirm D-01 names, D-02 baseline id and D-05 prefix before the new repo exists** - none (decision only; human: proceed-and-push)
2. **Task 2: Tracer (plugin repo + ROADMAP)** - `dac506a` (feat, sibling) and `f984e5e` (docs, summercms.go)
3. **Task 3: Groups, events, chat adapter, GDPR, remaining routes** - `2480ff0` (feat, sibling)
**Plan metadata:** pending-summary-commit (docs: complete plan)
**Sibling HEAD:** `2480ff0` on `origin/main`.
## Files Created/Modified
- `../sm-userfriends-plugin/` - full plugin tree (go.mod Go 1.27.0, analog pins, replace summercms => ../summercms.go; go.work also replaces sm-user-plugin and sm-chat-plugin)
- `.planning/ROADMAP.md` - Phase 19.1 Repos, SC2, SC3 wording
## Decisions Made
- Task 1 reply verbatim: proceed-and-push.
- Locked D-01 / D-02 / D-05 as named in the continuation.
- After Task 3 verify was green, `tea repos create --owner golem15 --name sm-userfriends-plugin` then `git push -u origin main` (never force).
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 2 - Correctness] Typed chat Registry Lookup requires a module import**
- **Found during:** Task 3
- **Issue:** PATTERNS analog is `app.Lookup[*chatcontracts.Registry]()`. Go cannot name that type without importing `sm-chat-plugin/contracts`, so `go.mod` gained `git.golem15.com/golem15/sm-chat-plugin`. The plan text said go.mod stay limited to the user plugin.
- **Fix:** Import contracts only (chat plugin `init` is not pulled). `Requires()` still lists only `golem15.user`. Lookup skip when chat is not Activated. `go.work` replace `../sm-chat-plugin`.
- **Files modified:** `../sm-userfriends-plugin/go.mod`, `go.work`, `boot.go`, `classes/friends_context_adapter.go`
- **Verification:** `grep -c golem15.chat` on `Requires()` is 0; `go test -short ./...` pass
- **Committed in:** `2480ff0`
**2. [Rule 2 - Correctness] GDPR heir also updates `owner_id`**
- **Found during:** Task 3
- **Issue:** PHP only promoted the heir's role. Postgres `golem15_userfriends_groups.owner_id` is `ON DELETE CASCADE`, so leaving `owner_id` on the deleting user would drop the group.
- **Fix:** After promoting the heir, set `group.owner_id = heir.user_id` before the user row goes.
- **Files modified:** `../sm-userfriends-plugin/classes/group_service.go` (`OnUserDeleting`)
- **Verification:** code review against the baseline FK; unit tests of the cascade land in plan 06
- **Committed in:** `2480ff0`
---
**Total deviations:** 2 auto-fixed (Rule 2 correctness)
**Impact on plan:** Required for a compiling soft adapter and for GDPR not to delete transferred groups. No scope creep.
## Issues Encountered
None beyond the deviations above. Task 1 was not re-asked. The sibling directory did not exist as a foreign module at Task 2 start.
## TDD Gate Compliance
N/A — TDD is not applicable for this plan (explicit in the execute dispatch). Tracer verify is automated-only (`HUMAN_VERIFY_MODE=end-of-phase`). After Task 2 automated verify passed: Tracer verified end-to-end — expanding.
## Auth gates
Task 1 `checkpoint:decision` `blocking-human` — user replied `proceed-and-push`. No further auth gates.
## Known Stubs
None.
## User Setup Required
None - no external service configuration required. The Gitea repo was created in-session.
## Next Phase Readiness
Ready for plan 19.1-02 (forum) subject to that plan's D-17 block. `sm-userfriends-plugin` is on `origin/main` at `2480ff0`. Plan 06 still owns GroupPolicy matrix tests and goldens.
## Self-Check: PASSED
- FOUND: `../sm-userfriends-plugin/plugin.go`
- FOUND: `../sm-userfriends-plugin/updates/202610180001_create_golem15_userfriends_baseline.go`
- FOUND: `../sm-userfriends-plugin/events/events.go`
- FOUND: `../sm-userfriends-plugin/http_tracer_test.go`
- FOUND: `.planning/ROADMAP.md` (webpush-go)
- FOUND: sibling commits `dac506a`, `2480ff0`; summercms.go `f984e5e`
- FOUND: remote `https://git.golem15.com/golem15/sm-userfriends-plugin` pushed `main` at `2480ff0`
---
*Phase: 19.1-chat-satellites*
*Completed: 2026-10-18*