docs(19.1-02): complete forum tracer plan

This commit is contained in:
Jakub Zych
2026-10-18 15:43:18 +02:00
parent 1f204f5a6c
commit 374088eccf
5 changed files with 287 additions and 14 deletions

View File

@@ -0,0 +1,241 @@
---
phase: 19.1-chat-satellites
plan: 02
subsystem: forum
tags: [forum, markdown, breakwater, jwt, postgres, gorm, testcontainers, deny-all]
# Dependency graph
requires:
- phase: 19
provides: analog sibling-plugin layout (boot steps, IF NOT EXISTS baseline, jwt.auth generic API, deny-all seam, test-only proof host)
- phase: 19.1
provides: plan 01 tea-create / proceed-and-push remote policy; D-17 breakwater.Render/Purify
provides:
- "new repository sm-forum-plugin (module git.golem15.com/golem15/sm-forum-plugin, package forum, plugin golem15.forum), pushed to git.golem15.com at 6a40030"
- "idempotent baseline 202610180001_create_golem15_forum_baseline (four PHP v1.1.0 tables)"
- "ForumService write path with breakwater.Render on write, stored content_html, DenyAll ForumAccessResolver, generic /_forum/api/v1 tracer"
- "DeleteContext, forum:rewrite-context-types, GDPR null authorship on user.Deleting"
affects: [19.1-06]
actuals:
tokens: 35796
tasks: 4
commits: 11
plan_head_before: eb341147d816a4e5ed254bcfe3370d4d79fdfcb9
plan_head_after: 1f204f5a6c2d2289d434105e0e22f94693ae5705
tech-stack:
added: []
patterns:
- "Forum consumes D-17 breakwater.Render/Purify; go.mod does not directly require bluemonday or goldmark"
- "ForumAccessResolver DenyAll default + setter nil→DenyAll + Boot Warn; test harness swaps allow-all after Boot"
- "DeleteContext is generic morph-pair soft-delete; host wires group-deleted; forum never imports userfriends"
key-files:
created:
- .planning/notes/2026-10-18-bluemonday-forum-policy.md
- ../sm-forum-plugin/plugin.go
- ../sm-forum-plugin/boot.go
- ../sm-forum-plugin/routes.go
- ../sm-forum-plugin/classes/forum_service.go
- ../sm-forum-plugin/classes/deny_all.go
- ../sm-forum-plugin/classes/lifecycle.go
- ../sm-forum-plugin/contracts/access_resolver.go
- ../sm-forum-plugin/console/rewrite_context_types.go
- ../sm-forum-plugin/updates/202610180001_create_golem15_forum_baseline.go
- ../sm-forum-plugin/http_tracer_test.go
- ../sm-forum-plugin/README.md
modified: []
key-decisions:
- "Task 2 reply verbatim: proceed"
- "Locked D-02 baseline id 202610180001_create_golem15_forum_baseline and D-05 prefix /_forum/api/v1; remote policy follows plan 01 proceed-and-push"
- "tea repos create --owner golem15 --name sm-forum-plugin succeeded; main pushed at 6a40030 (never force). summercms.go was not pushed."
patterns-established:
- "forum proof host: golem15.user + golem15.forum + a test-only teams fixture; harness SetAccessResolver(allow-all) after Boot; TestAccessResolverDefault asserts DenyAll before that swap"
requirements-completed: [SC-19.1-1, D-01, D-02, D-03, D-04, D-05, D-08, D-16, D-17, D-18]
coverage:
- id: D1
description: "D-16 named bluemonday forum-policy note (v1.0.27, PHP allow-list, nofollow, target blank, not richeditor UGC)"
requirement: D-16
verification:
- kind: other
ref: ".planning/notes/2026-10-18-bluemonday-forum-policy.md"
status: pass
human_judgment: false
- id: D2
description: "D-17 Render/Purify consumed from modules/breakwater; forum go.mod has no direct require of bluemonday or goldmark"
requirement: D-17
verification:
- kind: other
ref: "grep func Render( and func Purify( in modules/breakwater/breakwater.go"
status: pass
human_judgment: false
- id: D3
description: "Human lock of D-02 baseline id and D-05 prefix /_forum/api/v1 before the sibling repo existed"
requirement: D-02
verification: []
human_judgment: true
rationale: "Task 2 was checkpoint:decision gate=blocking-human; the user replied proceed and that lock cannot be asserted by a unit test"
- id: D4
description: "sm-forum-plugin tracer: category+thread+GFM table post; stored content_html has table and no script; mark read; unread 0"
requirement: D-05
verification:
- kind: unit
ref: "go -C ../sm-forum-plugin test -count=1 -short ./..."
status: pass
- kind: integration
ref: "../sm-forum-plugin/http_tracer_test.go#TestHTTPTracer"
status: pass
human_judgment: false
- id: D5
description: "DeleteContext, forum:rewrite-context-types, GDPR null authorship; no userfriends import"
requirement: D-08
verification:
- kind: unit
ref: "go -C ../sm-forum-plugin test -count=1 -short ./..."
status: pass
- kind: other
ref: "grep DeleteContext, forum:rewrite-context-types, DeletingEvent; TestNoUserfriendsImport"
status: pass
human_judgment: false
- id: D6
description: "Gitea remote golem15/sm-forum-plugin created and main pushed (never force)"
requirement: SC-19.1-1
verification:
- kind: other
ref: "tea repos create --owner golem15 --name sm-forum-plugin; git push -u origin main"
status: pass
human_judgment: false
duration: 9min
completed: 2026-10-18
status: complete
---
# Phase 19.1 Plan 02: Forum repo, baseline, GFM tracer, DeleteContext Summary
**Sibling `sm-forum-plugin` consumes `breakwater.Render`/`Purify` (D-17), ships the v1.1.0 IF NOT EXISTS baseline, a deny-all ForumAccessResolver, a GFM table HTTP tracer under `/_forum/api/v1`, generic `DeleteContext`, `forum:rewrite-context-types`, and GDPR null authorship.**
## Performance
- **Duration:** 9 min (this continuation; Task 1–2 ran in the prior wave)
- **Started:** 2026-10-18T13:30:10Z
- **Completed:** 2026-10-18T13:39:35Z
- **Tasks:** 4
- **Files modified:** 35 in the sibling plugin plus the D-16 note in summercms.go
## Task 2 human reply (verbatim)
proceed
Meaning recorded at resume: lock D-02 baseline id `202610180001_create_golem15_forum_baseline` and D-05 prefix `/_forum/api/v1`; implement; remote policy follows plan 01 proceed-and-push. After Task 4 is green, tea-create the remote and push main (never force). summercms.go is not pushed. Task 1 and Task 2 were not re-asked.
## Accomplishments
- D-16 note `.planning/notes/2026-10-18-bluemonday-forum-policy.md` (commit `2740ab2`) names bluemonday v1.0.27, the PHP allow-list, schemes http/https/mailto, RequireNoFollowOnLinks, AddTargetBlankToFullyQualifiedLinks, and is not the richeditor UGC policy.
- D-17 asserted before the forum repo: `modules/breakwater` exports `Render(markdown string) string` and `Purify(html string) string`. Printed `d17-ready`.
- New independent git repo `../sm-forum-plugin`: module `git.golem15.com/golem15/sm-forum-plugin`, package `forum`, PluginID `golem15.forum`, `Requires()` = `golem15.user` only. Never requires chat or userfriends.
- Idempotent baseline `202610180001_create_golem15_forum_baseline`: `golem15_forum_categories` (context_type NOT NULL, no FK, unique owner+slug), threads, posts, thread_reads. threads/posts `user_id` nullable SET NULL. `deletion_reason` varchar CHECK self|moderator or null.
- `contracts.AccessResolver`: CanRead / CanPost / CanManage / CanManageContext. `classes.DenyAll` all false. `New` and `SetAccessResolver(nil)` install DenyAll. Boot Warns while default is active.
- Write path `CreateCategory`, `CreateThread`, `Reply` inside `lagoon.Transaction`. New/edited posts call `breakwater.Render` and store `content_html`. GET returns stored HTML (D-18).
- Generic API `forum.APIPrefix` (`/_forum/api/v1`) behind `jwt.auth` and `forum-api` 120/min.
- Test host: `golem15.user` + `golem15.forum` + `acme.teams`. After Boot the harness installs allow-all. `TestAccessResolverDefault` (short) asserts DenyAll. `TestHTTPTracer` (Docker): GFM table + script tag; stored `content_html` has a table and no script; mark read; unread 0.
- `ForumService.DeleteContext(ctx, type, id)` soft-deletes categories (and cascaded threads/posts) for the morph pair. `forum:rewrite-context-types` walks `RegisterContext` LegacyPHPClass → alias, idempotent. GDPR: Listen `user.Deleting`; null `threads.user_id` and `posts.user_id`; keep bodies.
- Remaining PHP methods: seedDefaultCategory, renameCategory, reorderCategories, deleteCategory, editPost, deletePost, deleteThread, unreadThreadCount, markThreadRead; HTTP PUT/DELETE categories, reorder, GET threads/posts, PUT/DELETE posts, DELETE threads, preview.
- Remote created: `https://git.golem15.com/golem15/sm-forum-plugin`. `git push -u origin main` succeeded (never force). summercms.go was not pushed.
## Task Commits
Each task was committed atomically:
1. **Task 1: D-16 forum-policy note and D-17 gate** - `2740ab2` (docs, summercms.go)
2. **Task 2: Confirm D-02 forum baseline id and D-05 prefix** - none (decision only; human: proceed). Halt record `ceabc9c` (docs, summercms.go)
3. **Task 3: End-to-end forum post — category, thread, GFM table, no script element** - `0fee845` (feat, sm-forum-plugin)
4. **Task 4: DeleteContext, morph rewrite, remaining routes, GDPR null authorship** - `6a40030` (feat, sm-forum-plugin)
**Sibling HEAD:** `6a40030` on `origin/main`.
**Plan metadata:** (this SUMMARY commit)
## Files Created/Modified
- `.planning/notes/2026-10-18-bluemonday-forum-policy.md` - D-16 named forum policy
- `../sm-forum-plugin/` - full plugin tree (go.mod Go 1.27.0, analog pins, replace summercms => ../summercms.go; go.work also replaces sm-user-plugin)
## Decisions Made
- Task 2 reply verbatim: proceed.
- Locked D-02 / D-05 as named in the continuation. Remote policy follows plan 01 proceed-and-push.
- After Task 4 verify was green, `tea repos create --owner golem15 --name sm-forum-plugin` then `git push -u origin main` (never force).
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Indirect bluemonday/goldmark in go.mod**
- **Found during:** Task 3
- **Issue:** Acceptance grep of `bluemonday|goldmark` in `go.mod` is non-zero because `go mod tidy` records them as `// indirect` through `summercms/modules/breakwater`. Direct `require (` does not list them; forum does not vendor either library.
- **Fix:** Left as transitive of D-17. No direct require. TestNoUserfriendsImport still greps friends. Documented here so the verifier does not treat the indirect lines as a new library.
- **Files modified:** `../sm-forum-plugin/go.mod`
- **Verification:** `grep` of the direct require block is empty; `go test -short` and `TestHTTPTracer` pass
- **Committed in:** `0fee845`
**2. [Rule 2 - Missing Critical] Task 4 methods live in `classes/lifecycle.go` plus `DeleteContext` on `forum_service.go`**
- **Found during:** Task 4
- **Issue:** Remaining PHP methods would have made `forum_service.go` too large; acceptance grep requires `DeleteContext` on `forum_service.go`.
- **Fix:** `DeleteContext` is on `forum_service.go`; other Task 4 methods are `classes/lifecycle.go` in the same package.
- **Files modified:** `../sm-forum-plugin/classes/forum_service.go`, `../sm-forum-plugin/classes/lifecycle.go`
- **Verification:** `grep -n 'func (s *ForumService) DeleteContext' classes/forum_service.go` prints one line
- **Committed in:** `6a40030`
---
**Total deviations:** 2 auto-fixed (1 blocking, 1 missing critical)
**Impact on plan:** Indirect deps are the correct way to consume D-17. File split does not change behavior.
## Issues Encountered
Task 1 and Task 2 were not re-asked. `../sm-forum-plugin` did not exist at Task 3 start. D-17 had landed in `modules/breakwater`. Docker was available; `TestHTTPTracer` ran.
## TDD Gate Compliance
N/A — TDD is not applicable for this plan. Tracer verify is automated-only (`HUMAN_VERIFY_MODE=end-of-phase`). After Task 3 automated verify passed: Tracer verified end-to-end — expanding.
## Auth gates
Task 2 `checkpoint:decision` `blocking-human` — user replied `proceed`. 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 19.1-06 (unit tests, goldens, PHP maps, `scripts/check-phase19.1.sh`) and remaining incomplete plan 19.1-06. Forum is adoptable: host must `SetAccessResolver`; `DeleteContext` is generic.
---
*Phase: 19.1-chat-satellites*
*Completed: 2026-10-18*
## Self-Check: PASSED
- FOUND: `.planning/notes/2026-10-18-bluemonday-forum-policy.md` (`2740ab2`)
- FOUND: `modules/breakwater` `func Render(` and `func Purify(` (d17-ready)
- FOUND: `../sm-forum-plugin/plugin.go` (`PluginID golem15.forum`, `Requires golem15.user`)
- FOUND: `../sm-forum-plugin/classes/forum_service.go` (`DeleteContext`)
- FOUND: `../sm-forum-plugin/console/rewrite_context_types.go` (`forum:rewrite-context-types`)
- FOUND: `../sm-forum-plugin/boot.go` (`DeletingEvent`)
- FOUND: `../sm-forum-plugin/updates/202610180001_create_golem15_forum_baseline.go`
- FOUND: `../sm-forum-plugin/http_tracer_test.go`
- FOUND: sibling commits `0fee845`, `6a40030`; summercms.go `2740ab2`, `ceabc9c`
- FOUND: remote `https://git.golem15.com/golem15/sm-forum-plugin` pushed `main` at `6a40030`
- FOUND: Task 2 human reply verbatim `proceed`