--- **Turnover — changeset-0.37.14 Session 8** **Version:** 0.37.14.20 | 3 commits (2 yours + 1 triage) | PR #226 **Branch:** `changeset-0.37.14` | Commit: `e2481a2` --- ## Commits this session - `b8e03c4` — Upload 3 new, 3 modified files (profile bootstrap, ICD drift audit) - `0ceb20a` — Delete Critical Review.md (renamed to FE-REWRITE-REVIEW-0.37.14.md) - `e2481a2` — Critical Review triage: RBAC fix, _unwrap, pipe wiring, menu/dashboard, CI ## What was done (triage commit) ### P0 Fixes (3) — Fix Before Merge 1. **RBAC inversion** (1 file, 3 lines) — `user-menu.js` lines 85/91/96: `!authenticated || sw?.isAdmin` was inverted, showing Admin/Team Admin/Debug to unauthenticated users. Fixed to `authenticated && sw?.isAdmin`. Backend already enforced 403, but UI now correctly gates visibility. 2. **`_unwrap` SDK contract** (1 file, 1 line) — `rest-client.js` line 101: condition required pagination fields (`page`, `total`, `per_page`) to unwrap `{data: [...]}` envelopes. 24 call sites defensively did `resp.data || resp || []`. Relaxed to just check `Array.isArray(json.data)`. 3. **Pipe/filter pipeline wiring** (3 files) — `sw.pipe._runPre/Stream/Render` was fully built but never invoked. Wired into: - `use-chat.js`: pre-send filter before completion call (can block/transform) - `use-stream.js`: per-chunk stream filter in SSE loop - `markdown.js`: render filter after sanitization + @mention highlighting ### P1 Fixes (5) — Fix Before Tag 4. **deleteMessage phantom API** (1 file) — No server DELETE `/channels/:id/messages/:msgId` route exists. Removed `onDelete` prop from ChatPane so delete button is hidden until backend is wired. 5. **Menu submenu positioning** (1 file) — `menu.js`: submenu anchored to parent menu root div instead of triggering item. Added `itemRefs` map, `setSubmenu` now stores `{ item, anchorEl }`, submenu renders at correct position. 6. **Dashboard recent items** (1 file) — `chat-workspace.js`: added `onNavigate` prop to ChatDashboard, recent items now have `onClick` handler + cursor pointer. 7. **Version string mismatch** (1 file) — `sdk/index.js` line 164: hardcoded `'v0.37.9'` replaced with template literal `` `v${sw._sdk}` ``. Console now shows correct version. 8. **CI frontend coverage** (1 file) — `ci.yaml`: glob changed from `src/js/*.js` (top-level only) to `find src/js -name '*.js'` (recursive). Uses `--input-type=module` for ES module syntax checking. ### P2/P3 Quick Fixes (4) + Additional (2) 9. **Pipe Promise guard** — `pipe.js` `_runChain`: detects async filters returning Promises, logs error + increments error counter, continues chain. 10. **useEffect no-deps documented** — `chat-pane/index.js` line 66: added comment explaining intentional missing dependency array (handle must reference latest closures). 11. **Dual toast container** — `base.html`: removed orphaned `#toastContainer` div (Preact toast primitive creates its own). 12. **Dead `esc()` function** — `admin/users.js` line 7: deleted unused function. 13. **WS message deduplication** — `use-chat.js` line 288: added `prev.some(m => m.id && m.id === msg.id)` check before appending from WebSocket. Prevents duplicates on reconnect. 14. **Optimistic message key stability** — `use-chat.js` line 103: optimistic user messages now get `id: 'tmp-' + Date.now()` so Preact key is always stable. ### Roadmap Updates - `docs/ROADMAP.md` v0.37.17 row: added "debug modal Preact rebuild (CR P2-5)" - `docs/ROADMAP.md` v0.37.18 row: added "sw.can() gates (P2-1), __USER__/__PAGE_DATA__ removal (P2-4), _getScale SDK (P3-3)" - `FE-REWRITE-REVIEW-0.37.14.md`: P0/P1 sections marked resolved, P2/P3 sections annotated with version assignments. ## Files changed (+353/−35, 21 files) | File | Change | |---|---| | src/js/sw/shell/user-menu.js | ±6 RBAC condition fix | | src/js/sw/sdk/rest-client.js | ±1 _unwrap condition | | src/js/sw/components/chat-pane/use-chat.js | +11/−2 pipe pre, dedup, temp ID | | src/js/sw/components/chat-pane/use-stream.js | +9/−1 pipe stream filter | | src/js/sw/components/chat-pane/markdown.js | +12 pipe render, doc comment | | src/js/sw/components/chat-pane/index.js | ±4 remove onDelete, useEffect doc | | src/js/sw/primitives/menu.js | +16/−7 submenu anchor refactor | | src/js/sw/surfaces/chat/chat-workspace.js | +8/−3 dashboard onClick, onNavigate | | src/js/sw/sdk/index.js | ±1 version template literal | | src/js/sw/sdk/pipe.js | +6 Promise guard | | src/js/sw/surfaces/admin/users.js | −2 dead code | | server/pages/templates/base.html | −1 orphaned toast div | | .gitea/workflows/ci.yaml | +14/−4 recursive glob + ES module | | docs/ROADMAP.md | ±4 v0.37.17/18 deferred items | | FE-REWRITE-REVIEW-0.37.14.md | ±10 resolution annotations | | VERSION | 0.37.14.20 | | server/handlers/profile_bootstrap.go | +117 (your commit) | | server/main.go | +4 (your commit) | | server/handlers/integration_test.go | +4 (your commit) | | ICD-DRIFT-AUDIT.md | +95 (your commit) | | docs/ICD/profile.md | +59 (your commit) | ## Verification results | Check | Result | |---|---| | Admin menu items visible for admin | PASS | | Channel messages load (unwrap working) | PASS | | Dashboard recent items clickable | PASS | | Delete button hidden | PASS | | Console version: `SDK v0.37.14` | PASS | | Zero console errors | PASS | | @mentions rendered correctly | PASS | | Docker build + server start | PASS | ## Deferred items — version mapping | Item | Assigned To | Description | |---|---|---| | P3-5: Double-unwrap cleanup | **DONE v0.37.14.21 (S9)** | 93 patterns (not 24) cleaned; API envelope normalization; 3 bug fixes | | P2-5: Debug modal rebuild | **v0.37.17** | Raw HTML + inline onclick in base.html → Preact component | | P2-1: sw.can() RBAC gates | **v0.37.18** | Gate action buttons with `sw.can()` across all surfaces | | P2-4: __USER__/__PAGE_DATA__ | **v0.37.18** | Remove dead Go template injection, migrate settings to sw.auth.policies | | P3-3: _getScale SDK | **v0.37.18** | Move sidebar DOM coupling to `sw.shell.getScale()` API | | P3-4: Notes markdown raw DOM | **documented** | Intentional for perf, comment added in markdown.js | ## Known issues (carried forward) 1. **Thinking tags lost on refresh** — `` content spills into message body after reload. DB stores raw, load path doesn't extract like streaming path does. 2. **Model settings select/unselect all** — no bulk toggle. Deferred to per-provider model management UI. 3. **Double-unwrap cleanup** — 24 call sites still have defensive `resp.data || resp || []`. Safe now but redundant. Next session. ## Open backlog 1. Double-unwrap cleanup (24 sites, next session) 2. Thinking tags persistence across refresh 3. ICD remaining failures (Venice stream, knowledge 403-vs-404) 4. Extension surface "? User" label 5. Admin panel audit 6. Model qualified display + per-provider management UI 7. SDK gaps (20+ missing endpoints) 8. Team-admin surface audit 9. Settings surface audit 10. Notes surface audit 11. deleteMessage server endpoint (server route + handler needed) ---