diff --git a/.planning/phases/19.1-chat-satellites/19.1-01-SUMMARY.md b/.planning/phases/19.1-chat-satellites/19.1-01-SUMMARY.md new file mode 100644 index 00000000..576b9931 --- /dev/null +++ b/.planning/phases/19.1-chat-satellites/19.1-01-SUMMARY.md @@ -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*