5.4.4 `watchersService` — self watch/unwatch + admin manage-others + view-access validation + auto-watch hooks (create, comment)
Estimate: 26m · Depends on: 5.4.1, 5.1.2
The watch mechanics. lib/services/watchersService.ts + typed errors + DTOs + HTTP-only routes.
watch(workItemId, ctx) / unwatch — ANYONE who can view the issue watches themselves (a viewer included — watching is not editing; the verified permission split). Idempotent (re-watching is a no-op, the unique absorbs it). addWatcher(workItemId, userId, ctx) / removeWatcher — the "Manage watchers" half: project admin + workspace admin/owner only; the target MUST be a workspace member who can view the issue — rejected with a typed 422 naming the reason (the mirror silently drops, a documented trap we fix). listWatchers(workItemId, { cursor }, ctx) — view-gated, paged, avatars/names + a total count (the header eye count rides the detail read — slot a bounded watcherCount + viewerIsWatching into getIssueDetail). NO revision entries (mirror: watching is not a field change).
Auto-watch hooks (the verified create-or-comment rule, constant-on): createWorkItem adds the creator as watcher inside the create tx; commentsService.addComment (5.1.2) adds the commenter inside the comment tx (idempotent — the unique). The Story-5.7 preference toggle is the documented seam (a userId-keyed opt-out the hooks will consult; not built here).
Acceptance criteria
- Self watch/unwatch (incl. viewer), idempotent; manage-others admin-gated with the typed no-view-access rejection (private-project non-member target → 422, never silent drop); list paged + view-gated; cross-workspace 404.
getIssueDetailcarrieswatcherCount+viewerIsWatchingwithout an extra round-trip; no revision rows from any watch path.- Creating an issue auto-watches the creator; commenting auto-watches the commenter; both inside the owning tx, both idempotent.
- One method = one tx; routes HTTP-only; coverage ≥90% incl. the permission matrix.
Context refs
- 5.4.1 watcher repo; the verified watcher contract in the Story 5.4 description (view-required, manage permission, autowatch)
workItemsService.createWorkItem+commentsService.addComment(5.1.2) — the txs the hooks joingetIssueDetail(the parallel-fetch slot); finding #44 (404-not-403)- Story 5.7 stub — the preference seam these hooks will consult later