---
**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** — `<think>` 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)

---
