From f1ff2f940bc60281ec0f58cb07978516df7c1a8f Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Sun, 18 Oct 2026 19:25:32 +0200 Subject: [PATCH] docs(19.1): re-verify after review fixes; mark WR-04 and WR-05 fixed --- .../19.1-REVIEW-DISPOSITION.md | 8 +- .../19.1-chat-satellites/19.1-VERIFICATION.md | 264 ++++++++---------- 2 files changed, 117 insertions(+), 155 deletions(-) diff --git a/.planning/phases/19.1-chat-satellites/19.1-REVIEW-DISPOSITION.md b/.planning/phases/19.1-chat-satellites/19.1-REVIEW-DISPOSITION.md index dc063e17..858f927e 100644 --- a/.planning/phases/19.1-chat-satellites/19.1-REVIEW-DISPOSITION.md +++ b/.planning/phases/19.1-chat-satellites/19.1-REVIEW-DISPOSITION.md @@ -17,11 +17,11 @@ findings: title: "Reconcile ends empty rooms with no grace, including ringing calls" - id: WR-04 severity: warning - disposition: open + disposition: fixed title: "Generic chatbots dispatch ignores IsAvailable" - id: WR-05 severity: warning - disposition: open + disposition: fixed title: "Chatvideo starter delete cascades the whole session and skips LiveKit cleanup" - id: IN-01 severity: info @@ -51,8 +51,8 @@ recorded: 2026-10-18T14:16:00Z | WR-01 | warning | open | 19.1-REVIEW.md | | WR-02 | warning | open | 19.1-REVIEW.md | | WR-03 | warning | open | 19.1-REVIEW.md | -| WR-04 | warning | open | 19.1-REVIEW.md | -| WR-05 | warning | open | 19.1-REVIEW.md | +| WR-04 | warning | fixed | 19.1-REVIEW.md | +| WR-05 | warning | fixed | 19.1-REVIEW.md | | IN-01 | info | open | 19.1-REVIEW.md | | IN-02 | info | open | 19.1-REVIEW.md | | IN-03 | info | open | 19.1-REVIEW.md | diff --git a/.planning/phases/19.1-chat-satellites/19.1-VERIFICATION.md b/.planning/phases/19.1-chat-satellites/19.1-VERIFICATION.md index f21c7260..1d83f4e9 100644 --- a/.planning/phases/19.1-chat-satellites/19.1-VERIFICATION.md +++ b/.planning/phases/19.1-chat-satellites/19.1-VERIFICATION.md @@ -1,6 +1,6 @@ --- phase: 19.1-chat-satellites -verified: 2026-10-18T14:15:00Z +verified: 2026-10-18T17:23:38Z status: passed score: 16/16 must-haves verified covered_files: @@ -21,13 +21,14 @@ covered_files: - .planning/phases/19.1-chat-satellites/19.1-06-PLAN.md - .planning/phases/19.1-chat-satellites/19.1-06-SUMMARY.md - README.md + - docs/plugins/extending.md - docs/services/markdown.md - modules/breakwater/README.md - modules/breakwater/breakwater.go - modules/breakwater/breakwater_test.go - modules/breakwater/example_test.go - scripts/check-phase19.1.sh -covered_digest: "v2:sha256:2311abea5e82d375f2b20a2ae8ba39cb0849574ac0d8cdfc27ac48b689d72069" +covered_digest: "v2:sha256:797292f0aec9329d38baf6ef682dfde8a386bc45e4d44f6eb73db115470cc310" behavior_unverified: 0 overrides_applied: 1 overrides: @@ -35,223 +36,184 @@ overrides: reason: "D-08 soft adapter is a typed Lookup of sm-chat-plugin/contracts.Registry; go.mod must list the module (docs/plugins/extending.md: the consumer imports the package that declares the interface). Requires() stays golem15.user only; missing chat is a Boot skip." accepted_by: "Jakub Zych" accepted_at: "2026-10-18T15:54:48Z" +re_verification: + previous_status: passed + previous_score: 16/16 + trigger: "stale (covered_changed): scripts/check-phase19.1.sh and docs/plugins/extending.md changed after the UAT; sibling fixes sm-chatbots-plugin b723f2f and sm-chatvideo-plugin 492c45f landed" + gaps_closed: [] + gaps_remaining: [] + regressions: [] decision_coverage: honored: 18 total: 18 not_honored: [] -human_verification: +human_verified: - test: "Accept an override that ../sm-userfriends-plugin/go.mod may require sm-chat-plugin so Boot can Lookup[*chatcontracts.Registry], while Plugin.Requires() stays []string{\"golem15.user\"} only." - expected: "Override recorded. No code change. Removing the go.mod require would make the D-08 soft adapter uncompilable." - why_human: "Plan 01 prohibition literally says go.mod never require sm-chat-plugin. The same plan's D-08 truth requires a typed Lookup of the chat Registry. Those two statements cannot both be true in Go. Implementation honors D-08 (Requires() is user-only; missing chat is a skip). A human must accept the deviation." + uat_row: 1 + recorded_at: "2026-10-18T15:54:54.000Z" --- # Phase 19.1: Chat satellites Verification Report **Phase Goal:** The remaining shared plugins that Wavepath's app plugin requires run on SummerCMS: chatbots, chatvideo, forum, herald and userfriends. -**Verified:** 2026-10-18T14:15:00Z -**Status:** human_needed -**Re-verification:** No — initial verification +**Verified:** 2026-10-18T17:23:38Z +**Status:** passed +**Re-verification:** Yes. The earlier report went stale (`covered_changed`) after the UAT. Since then: a goldens secret scan was added to the gate (8643195), the gate now requires the chatvideo user-deletion test (533fb7c), a docs paragraph was added (97daec5), and two review fixes landed in sibling repos: chatbots WR-04 (b723f2f) and chatvideo WR-05 (492c45f). -Roadmap `**Mode:** mvp` is set, but the ROADMAP goal is not a user-story sentence (`user-story.validate` → `false`). Verification proceeded because execute-phase `verify_phase_goal` asked for this report and each plan already carries an application-mounts-satellites user story. A `/msd mvp-phase 19.1` rewrite of the ROADMAP goal is optional bookkeeping, not a code gap. +Roadmap `**Mode:** mvp` is set, but the ROADMAP goal is not a user-story sentence. Each plan carries its own application-mounts-satellite user story, and those stories are aggregated below. Rewriting the goal as a user story would only be bookkeeping. No code is missing. -This is an infrastructure/plugin-port phase (sibling repos, generic JWT APIs, no end-user UI). Human verification is therefore limited to the one planner-prohibition contradiction below — not invented UI walkthroughs. +## What changed since the previous report, and what was re-checked + +| Change | Repo / commit | Re-check performed | Result | +|--------|---------------|--------------------|--------| +| Gate docs stage: `detect_secrets` over five `testdata/goldens` trees; six `--self-test` plants | summercms.go 8643195 | `--self-test` exit 0; `--docs` exit 0 printing `goldens: no secrets in five sibling fixture trees` | ✓ | +| Gate postgres stage requires `TestUserDeletionReassignsSession`; anchored self-test pin on `PG_CHATVIDEO_TESTS` | summercms.go 533fb7c | `--postgres` (Docker) ran it: `3 top-level tests passed in ../sm-chatvideo-plugin (... TestUserDeletionReassignsSession)` | ✓ | +| Optional-dependency `go.mod` paragraph | summercms.go 97daec5 | `go test ./cmd/summer -run '^TestDocsTree$'` ok | ✓ | +| Chatbots: `Dispatch` returns nil when `!cmd.IsAvailable(actor, cmdCtx)` | sm-chatbots-plugin b723f2f | Read `classes/dispatcher.go:42`; `TestDispatchRefusesUnavailable` PASS (-short); `TestHTTPTracer` (postgres) posts `/secret` + `/no-such-trigger` and passed | ✓ | +| Chatvideo: `user.Deleting` listener (boot step `listeners`), `started_by` heir, LiveKit cleanup in `lagoon.AfterCommit` | sm-chatvideo-plugin 492c45f | Read `classes/user_deletion.go`, `boot.go`; `TestUserDeletionReassignsSession` (fires `DeletingEvent` through `app.Events` inside `lagoon.Transaction`, real FK CASCADE) passed in the postgres stage | ✓ | + +sm-userfriends-plugin, sm-forum-plugin and sm-herald-plugin have no commits since the previous report (14:15Z) and no uncommitted edits. All five sibling working trees are clean. ## User Flow Coverage -Aggregated from the six plan user stories (ROADMAP goal is not a user story): - | Step | Expected | Evidence | Status | |------|----------|----------|--------| -| Mount Friends | Host binds no extra seam; JWT `/_userfriends/api/v1` request/accept/list works | `../sm-userfriends-plugin/routes.go`; `TestHTTPTracer` passed (postgres) | ✓ | -| Mount Forum | Host binds `ForumAccessResolver`; context-scoped categories; GFM stored, XSS stripped | `../sm-forum-plugin/routes.go`; `breakwater.Render` on write; `TestHTTPTracer` passed | ✓ | -| Mount Chat bots | Host registers slash commands; dispatch invoker is the JWT actor | `../sm-chatbots-plugin/api/commands.go` uses `actor()` / `bouncer.User`; `TestHTTPTracer` passed | ✓ | -| Mount Herald | Host stores subscriptions and sends Web Push without PHP | `../sm-herald-plugin/routes.go`; `TestHTTPTracer` + `TestSendEventRollback` passed | ✓ | -| Mount Chat video | Host starts/joins/ends conversation video; stale sessions reconcile | `../sm-chatvideo-plugin/routes.go`; `TestHTTPTracer` + `TestReconcileEmptyRoom` passed | ✓ | -| Fail-closed gate | Skipped/missing named tests fail `scripts/check-phase19.1.sh` | `--self-test`, `--plugins`, `--docs`, `--postgres` all exit 0 this session | ✓ | +| Mount Friends | JWT `/_userfriends/api/v1` request/accept/list; no extra host seam | `routes.go` (`APIPrefix = "/_userfriends/api/v1"`); `TestHTTPTracer` passed (postgres, this run) | ✓ | +| Mount Forum | Host binds `ForumAccessResolver`; context-scoped categories; GFM stored, XSS stripped | `breakwater.Render` on write only (`forum_service.go:281,330`, `lifecycle.go:232`, preview `:387`); `TestHTTPTracer` passed | ✓ | +| Mount Chat bots | Host registers slash commands; dispatch invoker is the JWT actor; restricted commands refused | `api/commands.go` `actor()` → `Dispatch(..., userID, ...)`; `IsAvailable` gate; `TestHTTPTracer` + `TestDispatchRefusesUnavailable` passed | ✓ | +| Mount Herald | Host stores subscriptions and sends Web Push without PHP | `TestHTTPTracer` + `TestSendEventRollback` passed (postgres) | ✓ | +| Mount Chat video | Start/join/end; stale sessions reconcile; deleting a user keeps others' call history | `TestHTTPTracer`, `TestReconcileEmptyRoom`, `TestUserDeletionReassignsSession` passed (postgres) | ✓ | +| Fail-closed gate | Skipped/missing named tests or secret-shaped goldens fail `scripts/check-phase19.1.sh` | `--self-test`, `--plugins`, `--docs`, `--postgres` all exit 0 this session | ✓ | ## Goal Achievement ### Observable Truths -Merged from ROADMAP success criteria (SC-19.1-1..4) plus PLAN `must_haves.truths` (D-01..D-18). Roadmap wording kept where a plan restated an SC. - | # | Truth | Status | Evidence | | --- | ------- | ---------- | -------------- | -| 1 | SC-19.1-1: each plugin ports schema, models, routes and events 1:1 against its PHP original | ✓ VERIFIED | Five sibling repos exist with Winter layout, `202610180001_create_golem15_*_baseline`, models, JWT routes, and (where applicable) festival/lighthouse events. PHP method maps: userfriends 31, forum 25, chatbots 22, herald 14 declarations / 13 unique, chatvideo 0 in-plugin + Wavepath generic-or-N/A. | -| 2 | SC-19.1-2: webpush-go, LiveKit, and bluemonday decision notes exist and name the locked modules/versions | ✓ VERIFIED | `.planning/notes/2026-10-18-webpush-go.md` (`SherClockHolmes/webpush-go` v1.4.0); `2026-10-18-livekit-server-sdk-go.md` (`server-sdk-go/v2` v2.19.0, pion/media-sdk/buildkit flag); `2026-10-18-bluemonday-forum-policy.md` (v1.0.27, PHP allow-list, not UGC). `check-phase19.1.sh --docs` passed. | -| 3 | SC-19.1-3: ROADMAP marks criterion 3 already met; fonoteka.go has no userfriends port | ✓ VERIFIED | ROADMAP criterion 3 text is the “already met” wording; Repos line names `summercms.go`. Grep of `../fonoteka.go` for `userfriends` / `golem15.userfriends` returned no matches. | -| 4 | SC-19.1-4 / D-04: last plan delivers unit tests, PHP method maps, goldens, fail-closed gate | ✓ VERIFIED | `TestPHPMethodMap` ×5, goldens in all five repos, `scripts/check-phase19.1.sh` self-test/plugins/docs/postgres all green this session. | -| 5 | D-01: module paths, plugin IDs, Requires() match the frozen contract | ✓ VERIFIED | `golem15.userfriends` Requires `golem15.user` only; `golem15.forum` Requires user; `golem15.chatbots` Requires chat+user (no AI plugin; `TestNoGolem` passed); `golem15.herald` Requires empty; `golem15.chatvideo` Requires chat. | -| 6 | D-02: one idempotent IF NOT EXISTS baseline per plugin; demo-seed IDs are comments | ✓ VERIFIED | Baseline IDs `202610180001_create_golem15_{userfriends,forum,chatbots,herald,chatvideo}_baseline`. Userfriends comment records v1.0.1 demo-seed and inserts no demo rows. `TestBaseline` ×5 passed under `-short`. | -| 7 | D-03: DenyAll access seams with Boot Warn + setter; GDPR cascades | ✓ VERIFIED | Forum/chatbots/herald DenyAll + Warn + setter. `TestAccessResolverDefault`, `TestBotAuthResolverDefault`, `TestPresenceGateDefault` exist. Postgres: `TestUserDeletingHeir` (admin then member by `role ASC, created_at ASC`), `TestUserDeletionAnonymizes` (null `user_id`, keep bodies), `TestUserDeletionCascadesBotFlag`. | -| 8 | D-05: generic JWT APIs under documented prefixes; actor from token; named buckets | ✓ VERIFIED | `/_userfriends/api/v1` (`community-join` 10/min on request/join); `/_forum/api/v1/contexts/{type}/{id}/...`; `/_chatbots/api/v1` commands+dispatch; `/_herald/api/v1/push/*`; `/_chatvideo/api/v1/conversations/{id}/video/{start,join,leave,end,status}` + `/call-links/{code}` (`video-initiate` 5/min on start only). Five `TestHTTPTracer` runs passed (postgres). | -| 9 | D-06: herald ships the generic API (PHP “no routes” reversed) | ✓ VERIFIED | `routes.go` mounts subscription + vapid-public-key. README states the reversal and D-06. | -| 10 | D-07: legacy `/api/friends`, `/api/video`, Winter components, bundled JS, demo seeders not registered | ✓ VERIFIED | Userfriends and chatvideo `Routes()` only mount `/_* /api/v1`. No `FriendsList` / `VideoCall` components. Demo seeders are comments. | -| 11 | D-08: group festival events; soft chat adapter; forum `DeleteContext`; forum never imports userfriends | ✓ VERIFIED | `userfriends.GroupCreated/GroupDeleted/MemberJoined/MemberLeft`; `Fire` inside write tx; `Lookup[*chatcontracts.Registry]` skip if absent; `Requires()` omits chat. `ForumService.DeleteContext` soft-deletes the morph pair. `TestNoUserfriendsImport` passed. | -| 12 | D-10 / D-11: PublicOnly subscribe/send; enqueue on caller tx; rollback sends 0; 410/404 prune | ✓ VERIFIED | `ValidateEndpoint` rejects non-HTTPS; `NewSender` uses `fetchguard.PublicOnlyMode`; `Options.HTTPClient = s.http`; `jobs.Enqueue` on caller `tx`; `TestSendEventRollback` passed; `Deliver` prunes 404/410. No `EndpointGuard` type. | -| 13 | D-12: VAPID config keys + `herald:vapid-generate`; private key not logged | ✓ VERIFIED | `golem15.herald.vapid.{subject,public_key,private_key}`. Command prints `VAPID_PUBLIC_KEY=` / `VAPID_PRIVATE_KEY=` via `bonfire.Output`, not slog. GET vapid-public-key returns public key only. | -| 14 | D-13 / D-14 / D-15: LiveKit RoomAPI, token golden, video authorizer segment 2, 5-minute reconciler | ✓ VERIFIED | `type RoomAPI interface`; `TrustedMode` to operator URL; `parseVideoSessionChannel` splits segment 2 (`video:session:{id}`), capabilities `["prs"]`; `TestLiveKitTokenClaims` + `TestVideoAuthorizer` passed (`-short`); `chatvideo:reconcile-sessions` + `golem15.chatvideo.reconcile_sessions` MaxAttempts 1 + `Every(5 * time.Minute)`; `TestReconcileEmptyRoom` passed. Unique `active_conversation_id`, `join_code`, nullable `card_message_id`. | -| 15 | D-16 / D-17 / D-18: breakwater `Render`/`Purify`; forum write-path render; adopted `content_html` served stored | ✓ VERIFIED | `modules/breakwater` exports both funcs; forum `go.mod` has no direct bluemonday/goldmark require (indirect via summercms). `forum_service.go` / `lifecycle.go` call `breakwater.Render` on write. `api/json.go` copies stored `ContentHTML`. `TestAdoptedContentHTML` + forum `TestHTTPTracer` (table present, no `