docs(tracker-web): record backend enablers for UX-12.3/13.1
Mark UX-12.3 (rich-text) and UX-13.1 (NotificationCenter) as 🔒 blocked-on-backend rather than open — they are excluded from the ✅ count and each now carries a one-paragraph spec of the exact platform-service change required: - UX-12.3: server-side HTML sanitization (allowlist tags/attrs; strip scripts/event-handlers/js: + data: URLs) on items.description + comments.body write paths, so RichTextEditor/RichTextViewer can be safely adopted. - UX-13.1: emit notifications into platform-service's existing notifications module on tracker events (new comment, status change, vote milestone) targeted to the item author/subscribers with productId, exposed via the /api/tracker proxy, so NotificationCenter binds a real feed. Add BACKEND_ENABLERS.md tracking both follow-ups (title, blocking item, target module, acceptance criteria, backward-compat constraint — platform-service is shared by 9 products). Update the Expand tracker line and notes to show all client-only waves complete and these two backend-blocked. Docs only — no source/dep/lockfile changes. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
81b5ed0baf
commit
060daa4883
87
dashboards/tracker-web/docs/roadmaps/BACKEND_ENABLERS.md
Normal file
87
dashboards/tracker-web/docs/roadmaps/BACKEND_ENABLERS.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# tracker-web — Backend Enablers
|
||||||
|
|
||||||
|
> Follow-ups that **cannot ship from `dashboards/tracker-web` alone** because they require a change
|
||||||
|
> to the shared `services/platform-service`. They are the only remaining items on the
|
||||||
|
> [UX integration roadmap](UX_INTEGRATION_BYTELYST.md) and are **excluded from its ✅ count** until
|
||||||
|
> the backend enabler lands.
|
||||||
|
>
|
||||||
|
> **Hard constraint for every item below:** `platform-service` is **shared by 9 products**
|
||||||
|
> (LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, FlowMonk, NoteLett, ActionTrail,
|
||||||
|
> EffoRise, LocalMemGPT — see `AGENTS.md`). Every change here **must be additive and
|
||||||
|
> backward-compatible**: no behavioural change for products that do not opt in, existing rows/reads
|
||||||
|
> keep working, and every persisted document keeps its `productId`.
|
||||||
|
|
||||||
|
| ID | Title | Blocks | Target module | Status |
|
||||||
|
| ---- | ----------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------- | -------------- |
|
||||||
|
| BE-1 | Server-side HTML sanitization for item/comment bodies | UX-12.3 (rich-text) | `services/platform-service` — items + comments write paths | 🔒 Not started |
|
||||||
|
| BE-2 | Tracker-event notifications feed | UX-13.1 (`NotificationCenter`) | `services/platform-service` — notifications module + `/api/tracker` proxy | 🔒 Not started |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BE-1 — Server-side HTML sanitization for `items.description` + `comments.body`
|
||||||
|
|
||||||
|
- **Title:** Sanitize rich HTML on the item-description and comment-body write paths.
|
||||||
|
- **Blocking roadmap item:** [UX-12.3](UX_INTEGRATION_BYTELYST.md#ux-12--detail--board-richness-tabs--tooltip--drawer--timeline--rich-text)
|
||||||
|
— adopt `@bytelyst/rich-text` `RichTextEditor` / `RichTextViewer` in tracker-web.
|
||||||
|
- **Target module:** `services/platform-service` — the **items** module (`items.description`) and the
|
||||||
|
**comments** module (`comments.body`), applied **server-side before persist** (create + update).
|
||||||
|
- **Why it's blocked:** Today `TrackerItem.description` and `Comment.body` are plain `string`s
|
||||||
|
rendered with `whitespace-pre-wrap`; the `/api/tracker/*` proxy neither stores nor sanitizes rich
|
||||||
|
HTML. Adopting a rich-text editor client-side would persist attacker-controlled HTML with no
|
||||||
|
server-side sanitization (stored-XSS), so it must not be done until the backend guarantees safety.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
- HTML is sanitized **on the server** (never trust the client) on every write to `items.description`
|
||||||
|
and `comments.body` (create and update).
|
||||||
|
- **Allowlist** of formatting tags only — e.g. `p`, `br`, `strong`, `em`, `u`, `s`, `a`,
|
||||||
|
`ul`/`ol`/`li`, `blockquote`, `code`, `pre`, `h1`–`h3`. Everything else is stripped/escaped.
|
||||||
|
- **Attribute allowlist:** only safe attributes survive; `a[href]` is restricted to
|
||||||
|
`http:` / `https:` / `mailto:` schemes (and gets `rel="noopener noreferrer"`).
|
||||||
|
- **Stripped unconditionally:** `<script>` / `<style>` / `<iframe>` / `<object>`, all inline
|
||||||
|
event-handler attributes (`on*`), and `javascript:` / `data:` URLs.
|
||||||
|
- Output is idempotent (sanitizing already-sanitized content is a no-op) and length-bounded as today.
|
||||||
|
- **Backward-compatible:** existing plain-text rows still read correctly; products that send plain
|
||||||
|
text are unaffected; the response shape is unchanged. **No behavioural change for the other 8
|
||||||
|
products** sharing `platform-service`.
|
||||||
|
- Unit tests cover the XSS vectors above (script injection, `onerror=`, `javascript:` href,
|
||||||
|
`data:` URI, mismatched/oversized tags).
|
||||||
|
|
||||||
|
### Unblocks (tracker-web side, once shipped)
|
||||||
|
|
||||||
|
- Swap the description/comment `<textarea>` for `RichTextEditor` (compose) and render saved content
|
||||||
|
with `RichTextViewer` — no client-side `dangerouslySetInnerHTML` of unsanitized content.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BE-2 — Tracker-event notifications feed
|
||||||
|
|
||||||
|
- **Title:** Emit + expose a per-user notifications feed for tracker events.
|
||||||
|
- **Blocking roadmap item:** [UX-13.1](UX_INTEGRATION_BYTELYST.md#ux-13--notifications-surface-via-bytelystnotifications-ui-stretch--data-gated)
|
||||||
|
— mount `NotificationCenter` (bell + `InboxItem` rows) in tracker-web.
|
||||||
|
- **Target module:** `services/platform-service` — its **existing notifications module** (event
|
||||||
|
emission) + the `/api/tracker` proxy (read surface).
|
||||||
|
- **Why it's blocked:** tracker exposes **no notifications feed** — the `/api/tracker/*` proxy
|
||||||
|
surfaces only items / comments / votes / roadmap. `NotificationCenter`/`InboxItem` have nothing to
|
||||||
|
bind to, so only the client-side `BannerStack`/`Announcement` (UX-13.2) shipped.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
- Tracker events emit notifications into the **existing** notifications module — at minimum:
|
||||||
|
**new comment**, **status change**, and **vote milestone** (e.g. crossing 10/25/50 votes).
|
||||||
|
- Each notification is **fanned out to the item author and any subscribers/watchers** of the item,
|
||||||
|
and is **stamped with `productId`** so the feed is product-scoped.
|
||||||
|
- A read API (list with pagination, unread count, mark-as-read / mark-all-read) is **exposed through
|
||||||
|
the `/api/tracker` proxy** so the dashboard reads it with the existing auth token.
|
||||||
|
- Payload shape is compatible with `@bytelyst/notifications-ui` `InboxItem` (id, title/body, type,
|
||||||
|
timestamp, read flag, deep-link to the item).
|
||||||
|
- **Additive / backward-compatible:** emitting these notifications causes **no behavioural change for
|
||||||
|
the 9 products that do not subscribe**; the notifications module contract is extended, not broken;
|
||||||
|
every notification document carries `productId`.
|
||||||
|
- Tests cover emission on each event type, author/subscriber targeting, product scoping, and the
|
||||||
|
unread-count / mark-as-read transitions.
|
||||||
|
|
||||||
|
### Unblocks (tracker-web side, once shipped)
|
||||||
|
|
||||||
|
- Bind `NotificationCenter` to the feed (bell + `InboxItem` rows) in the AppShell sidebar/header,
|
||||||
|
reading via the `/api/tracker` proxy.
|
||||||
@ -275,27 +275,52 @@ pnpm build # final gate
|
|||||||
activity/comment history with `Timeline`.
|
activity/comment history with `Timeline`.
|
||||||
(Edit + Delete now live in an `ActionMenu` in the page header; comments render via
|
(Edit + Delete now live in an `ActionMenu` in the page header; comments render via
|
||||||
`Timeline`. UX-12.2 `32dac7d4`: tc/lint/test 162 ✓/build/e2e 18 ✓.)
|
`Timeline`. UX-12.2 `32dac7d4`: tc/lint/test 162 ✓/build/e2e 18 ✓.)
|
||||||
- [ ] **12.3** _(stretch — needs HTML-capable description/comment storage)_ Swap the plain
|
- [ ] 🔒 **12.3 — BLOCKED ON BACKEND** _(stretch — needs HTML-capable description/comment storage)_
|
||||||
description/comment `<textarea>` for `RichTextEditor`, and render saved content with
|
Swap the plain description/comment `<textarea>` for `RichTextEditor`, and render saved content
|
||||||
`RichTextViewer`. **Only do this if** the backend stores/returns rich HTML safely;
|
with `RichTextViewer`. **Only do this if** the backend stores/returns rich HTML safely.
|
||||||
otherwise leave `- [ ]` with a note. **Verify per task:** `pnpm typecheck && pnpm lint && pnpm build`.
|
**Verify per task:** `pnpm typecheck && pnpm lint && pnpm build`.
|
||||||
DEFERRED (data-gated): `TrackerItem.description` and `Comment.body` are plain `string`s
|
DEFERRED (data-gated): `TrackerItem.description` and `Comment.body` are plain `string`s
|
||||||
rendered with `whitespace-pre-wrap`; the `/api/tracker/*` proxy does not store or sanitize
|
rendered with `whitespace-pre-wrap`; the `/api/tracker/*` proxy does not store or sanitize
|
||||||
rich HTML. Adopting `RichTextEditor` would persist HTML with no backend sanitization (XSS
|
rich HTML. Adopting `RichTextEditor` would persist HTML with no backend sanitization (XSS
|
||||||
risk) and mismatch the plain-text model, so `@bytelyst/rich-text` is intentionally not
|
risk) and mismatch the plain-text model, so `@bytelyst/rich-text` is intentionally not
|
||||||
adopted until the backend supports safe rich HTML. No dep added.
|
adopted until the backend supports safe rich HTML. No dep added. **Kept out of the ✅ count.**
|
||||||
|
|
||||||
|
> **Required platform-service change** (see [`BACKEND_ENABLERS.md`](BACKEND_ENABLERS.md) §BE-1):
|
||||||
|
> Add server-side HTML sanitization on the **write** paths for `items.description` and
|
||||||
|
> `comments.body` in `services/platform-service` (the items + comments modules, before persist).
|
||||||
|
> The sanitizer must apply a strict allowlist of formatting tags (e.g. `p`, `br`, `strong`,
|
||||||
|
> `em`, `u`, `s`, `a`, `ul`/`ol`/`li`, `blockquote`, `code`, `pre`, headings) and safe attributes
|
||||||
|
> (`a[href]` restricted to `http`/`https`/`mailto`), and **strip** all `<script>`/`<style>`,
|
||||||
|
> inline event-handler attributes (`on*`), and `javascript:`/`data:` URLs. Sanitization must run
|
||||||
|
> on the server (never trust the client), preserve the existing plain-text reads for old rows,
|
||||||
|
> and be backward-compatible since the field is shared by every product. Once stored HTML is
|
||||||
|
> guaranteed safe, tracker-web can adopt `RichTextEditor` (compose) + `RichTextViewer` (render)
|
||||||
|
> with no client-side `dangerouslySetInnerHTML` of unsanitized content.
|
||||||
|
|
||||||
## UX-13 — Notifications surface via `@bytelyst/notifications-ui` (stretch / data-gated)
|
## UX-13 — Notifications surface via `@bytelyst/notifications-ui` (stretch / data-gated)
|
||||||
|
|
||||||
> **Gate:** only build this if tracker has (or will get) a notifications data source. If there
|
> **Gate:** only build this if tracker has (or will get) a notifications data source. If there
|
||||||
> is no feed, ship just the `BannerStack` for client-side system messages and leave the rest `- [ ]`.
|
> is no feed, ship just the `BannerStack` for client-side system messages and leave the rest `- [ ]`.
|
||||||
|
|
||||||
- [ ] **13.1** Add `@bytelyst/notifications-ui`; mount a `NotificationCenter` bell in the header
|
- [ ] 🔒 **13.1 — BLOCKED ON BACKEND** Add `@bytelyst/notifications-ui`; mount a `NotificationCenter`
|
||||||
(or AppShell from UX-8) fed by the notifications API, with `InboxItem` rows.
|
bell in the header (or AppShell from UX-8) fed by the notifications API, with `InboxItem` rows.
|
||||||
DEFERRED (data-gated): tracker exposes no notifications feed — the `/api/tracker/*` proxy
|
DEFERRED (data-gated): tracker exposes no notifications feed — the `/api/tracker/*` proxy
|
||||||
surfaces only items/comments/votes/roadmap, with no notifications endpoint. Per the wave
|
surfaces only items/comments/votes/roadmap, with no notifications endpoint. Per the wave
|
||||||
gate, `NotificationCenter`/`InboxItem` are left unbuilt until a feed exists. The dep is
|
gate, `NotificationCenter`/`InboxItem` are left unbuilt until a feed exists. The dep is
|
||||||
added and the imports are smoke-tested so a future feed wiring starts from green.
|
added and the imports are smoke-tested so a future feed wiring starts from green.
|
||||||
|
**Kept out of the ✅ count.**
|
||||||
|
|
||||||
|
> **Required platform-service change** (see [`BACKEND_ENABLERS.md`](BACKEND_ENABLERS.md) §BE-2):
|
||||||
|
> Emit notifications into platform-service's **existing notifications module** on tracker
|
||||||
|
> events — at minimum **new comment**, **status change**, and **vote milestones** — fanned out
|
||||||
|
> to the item author and any subscribers/watchers, each stamped with `productId` so the feed
|
||||||
|
> stays product-scoped. Expose the resulting feed (list + unread count + mark-as-read) through
|
||||||
|
> the `/api/tracker` proxy so the dashboard can read it with the existing token, and keep the
|
||||||
|
> payload shape compatible with `@bytelyst/notifications-ui` `InboxItem`. The change must be
|
||||||
|
> additive/backward-compatible (platform-service is shared by 9 products — no behavioural change
|
||||||
|
> for products that do not subscribe). Once the feed exists, tracker-web binds `NotificationCenter`
|
||||||
|
> to it (bell + `InboxItem` rows) in the AppShell sidebar/header.
|
||||||
|
|
||||||
- [x] **13.2** Use `BannerStack` for top-of-page system/maintenance messages and `Announcement`
|
- [x] **13.2** Use `BannerStack` for top-of-page system/maintenance messages and `Announcement`
|
||||||
for a dismissible "what's new" pill. **Verify:** `pnpm typecheck && pnpm lint && pnpm build`.
|
for a dismissible "what's new" pill. **Verify:** `pnpm typecheck && pnpm lint && pnpm build`.
|
||||||
(Added `@bytelyst/notifications-ui` workspace dep with a minimal `link:` lockfile entry;
|
(Added `@bytelyst/notifications-ui` workspace dep with a minimal `link:` lockfile entry;
|
||||||
@ -348,9 +373,17 @@ pnpm build # final gate
|
|||||||
|
|
||||||
```
|
```
|
||||||
Core : UX-1 ✅ UX-2 ✅ UX-3 ✅ UX-4 ✅ UX-5 ✅ UX-6 ✅ UX-7 ✅ UX-8 ✅
|
Core : UX-1 ✅ UX-2 ✅ UX-3 ✅ UX-4 ✅ UX-5 ✅ UX-6 ✅ UX-7 ✅ UX-8 ✅
|
||||||
Expand : UX-9 ✅ UX-10 ✅ UX-11 ✅ UX-12 ✅ UX-13 ✅ (stretch: 12.3, 13.1 deferred — see notes)
|
Expand : UX-9 ✅ UX-10 ✅ UX-11 ✅ UX-12 ✅ UX-13 ✅
|
||||||
|
Backend-blocked (not in ✅ count) : UX-12.3 🔒 UX-13.1 🔒 (see BACKEND_ENABLERS.md)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**All client-only waves are complete.** Every task that can ship from `dashboards/tracker-web`
|
||||||
|
alone (Core UX-2 … UX-8 and Expand UX-9 … UX-13) is done. The only remaining items — **UX-12.3**
|
||||||
|
(rich-text) and **UX-13.1** (notifications feed) — are **🔒 blocked on a shared `platform-service`
|
||||||
|
change**, not on tracker-web. They are tracked as follow-ups in
|
||||||
|
[`BACKEND_ENABLERS.md`](BACKEND_ENABLERS.md) and are intentionally excluded from the ✅ count until
|
||||||
|
the backend enabler lands.
|
||||||
|
|
||||||
**UX-1 is done** (token bridge + Primitives adapter, commit `dc01dd02`) — the `--bl-*` bridge is
|
**UX-1 is done** (token bridge + Primitives adapter, commit `dc01dd02`) — the `--bl-*` bridge is
|
||||||
live, so shared components already inherit tracker's theme. **Start with UX-2.** Before editing,
|
live, so shared components already inherit tracker's theme. **Start with UX-2.** Before editing,
|
||||||
read `src/app/globals.css` (the `--bl-*` bridge), `src/components/ui/Primitives.tsx` (the adapter
|
read `src/app/globals.css` (the `--bl-*` bridge), `src/components/ui/Primitives.tsx` (the adapter
|
||||||
@ -379,8 +412,12 @@ conventional commit referencing its roadmap ID:
|
|||||||
| UX-12.2 | `32dac7d4` | item-detail `ActionMenu` + `Timeline` comments |
|
| UX-12.2 | `32dac7d4` | item-detail `ActionMenu` + `Timeline` comments |
|
||||||
| UX-13.2 | `3d22c303` | `BannerStack` + `Announcement` system messaging |
|
| UX-13.2 | `3d22c303` | `BannerStack` + `Announcement` system messaging |
|
||||||
|
|
||||||
**Deferred (data-gated, documented above):** UX-12.3 (`RichTextEditor` — backend stores plain
|
**🔒 Blocked on backend (data-gated, not in the ✅ count):** UX-12.3 (`RichTextEditor` — needs
|
||||||
text, no HTML sanitization) and UX-13.1 (`NotificationCenter` — no notifications feed). Both deps
|
server-side HTML sanitization on `items.description` + `comments.body`) and UX-13.1
|
||||||
that are needed are wired/smoke-tested so a future enablement starts from green.
|
(`NotificationCenter` — needs a real notifications feed). Both are owned follow-ups tracked in
|
||||||
|
[`BACKEND_ENABLERS.md`](BACKEND_ENABLERS.md); the needed client deps are already wired/smoke-tested
|
||||||
|
so enablement starts from green once the shared `platform-service` change ships.
|
||||||
|
|
||||||
**Out of scope for this run:** the **Core** waves UX-2 … UX-8 remain `⬜`.
|
**Core waves UX-2 … UX-8 are complete** (see the per-item SHAs above) — every wave that can ship
|
||||||
|
from `dashboards/tracker-web` alone is done. Nothing in this roadmap remains that is unblocked at
|
||||||
|
the tracker-web layer.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user