Artifact a3420bc66bbb75017de401f71e7ae2c12097948f9138fa62566a73a4d6aa1cd0:
- File
internal/oltp/category.go
— part of check-in
[8aff98bb76]
at
2026-08-21 19:29:39
on branch quarry-forum
— forum announcements: maintainer-only category, pinned strip, home surfacing
Category is OLTP policy on an untouched artifact (fossil peers see a plain thread); forum.announce enforces who-may-post via op auth level. (user: dmestas, size: 1840) [annotate] [blame] [check-ins using] [more...]
// category.go stores forum thread categorization — forge POLICY, not // content (0001 §8): the forum artifact is untouched, so a fossil peer // sees a plain thread, and category semantics live entirely here. v1 // has one special category, "announcements"; a policy table for custom // categories arrives when custom categories do. package oltp const categorySchema = ` CREATE TABLE IF NOT EXISTS forum_thread_category ( repo TEXT NOT NULL, thread TEXT NOT NULL, -- thread root hash category TEXT NOT NULL, PRIMARY KEY (repo, thread) );` // Announcements is the built-in category: maintainer-start, anyone- // reply, pinned at the top of the forum and surfaced on the repo home. const Announcements = "announcements" // SetThreadCategory records (replacing) one thread's category; an empty // category uncategorizes. func (d *DB) SetThreadCategory(repo, thread, category string) error { if category == "" { _, err := d.db.Exec(`DELETE FROM forum_thread_category WHERE repo = ? AND thread = ?`, repo, thread) return err } _, err := d.db.Exec(`INSERT INTO forum_thread_category (repo, thread, category) VALUES (?, ?, ?) ON CONFLICT (repo, thread) DO UPDATE SET category = excluded.category`, repo, thread, category) return err } // ThreadCategories returns every categorized thread of a repo. One map // per page render: category assignment is small by construction (only // special threads carry one). func (d *DB) ThreadCategories(repo string) (map[string]string, error) { rows, err := d.db.Query(`SELECT thread, category FROM forum_thread_category WHERE repo = ?`, repo) if err != nil { return nil, err } defer rows.Close() out := map[string]string{} for rows.Next() { var t, c string if err := rows.Scan(&t, &c); err != nil { return nil, err } out[t] = c } return out, rows.Err() }