Reviewers' Commenting System

Feature Owner: scorevi (Sean Patrick Caintic)
Module: Review Workflow
Priority: P0
Sprint #12: Fully Implemented & Deployed
Date: 2026-06-29


EXECUTIVE SUMMARY

What is this feature?
Canvas-integrated threaded commenting system for content review. Reviewers and authenticated users attach comments to specific canvas nodes. Threaded replies, resolution tracking, bulk operations, content sanitization, and audit logging via deletion trigger. Powered by CommentingContext (components/context/CommentingContext.tsx, 47 lines) exposing useCommenting() and useCommentingOptional() hooks.

Why does it matter?
Text feedback on specific content elements is the core of the review workflow. A reviewer must be able to click a quiz node and say "Question 3 has the wrong answer." Without node-level commenting, feedback is disconnected from the content.

What's the MVP scope?
Fully deployed. Threaded comments on canvas nodes. Resolution tracking. Bulk resolve/delete. CSV/JSON export. Per-node comment analytics. Content sanitization. Audit trail via comment_deletion_log. Fire-and-forget notifications. Approximately 16+ API routes.


1. USER PAIN POINT & SOLUTION

Current State (Without Feature)

Reviewers have no way to leave feedback on specific content elements. Feedback is given via email, Slack, or spreadsheets — disconnected from the actual quest nodes.

Pain Point

Emotional: "I reviewed this quest but my feedback is scattered across emails. Did they fix question 3?"
Functional: No in-platform feedback mechanism.
Business Impact: Review cycles are slow and disorganized; feedback is lost or ignored.

Future State (With Feature)

Reviewer clicks a node → opens comment panel → writes feedback → creator is notified → creator resolves comment → reviewer approves. All feedback tracked per node.

Marketing Hook

"Click a node. Leave a comment. Resolve it. Every piece of feedback lives where it matters."


2. 4D FRAMEWORK MAPPING

Diagnose

Comment analytics (per-node heatmap) help diagnose content trouble spots.

Design

Node-level comments guide the content revision process.

Develop

Creators address comments during the Design/Develop loop.

Deliver

Comment export (CSV/JSON) provides an auditable feedback trail for compliance.


3. USER FLOWS

Entry Point

Reviewer opens a claimed quest in read-only canvas mode. CommentPanel.tsx available as floating panel.

Success Criteria

Comments created, replied to, resolved, and optionally exported.

Main Flow (Happy Path)

  1. Reviewer opens quest in canvas → CommentPanel.tsx (479 lines) appears

  2. Clicks a node → CommentPanel filters to that node's comments

  3. CommentThread.tsx (296 lines) shows threaded discussion with node_id mapping

  4. Writes comment via CommentForm.tsx (209 lines) → POST /api/reviewer/comments/create

    • Content sanitized by sanitizeCommentContent() — strips HTML/script tags

    • Node IDs mapped to human names via lib/utils/node-display-name.ts (122 lines)

  5. Creator notified: fire-and-forget notification via notifyCreatorOfNewComment

  6. Creator replies → notifyParentAuthorOfReply fires if parent exists

  7. After revision, comment marked is_resolved = TRUE via PATCH /api/reviewer/comments/[commentId]

  8. Export available: POST /api/reviewer/comments/export (CSV or JSON)

Edge Cases

  • No data: No comments on node → "Be the first to leave feedback" prompt.

  • API error: Content sanitization rejects HTML tags → stripped automatically.

  • Permission denied: Non-author trying to delete → "Only the comment author can delete this."

  • Rate limited: Guest exceeding 20 comments/hour limit → 429 response.

Decision Points

  • Unresolved comments block quest approval (enforced in review workflow).


4. INFORMATION ARCHITECTURE

Primary Information (Always visible)

  • CommentPanel: Comment threads organized by node, unresolved count badge.

  • CommentThread: Node name (e.g., "TEXT 4"), author avatar/name, content, timestamp.

Secondary Information

  • Resolution status indicator.

  • "Go to Node" button (spotlights node on canvas).

  • Analytics tab via CommentAnalyticsView.tsx (230 lines) — resolution summary + per-node breakdown.

Actions

Primary CTA: "Add Comment", "Resolve".
Secondary Actions: "Reply", "Delete", "Export Feedback", "Bulk Resolve/Delete".


5. WIREFRAMES

Excluded — UI is fully developed (10+ components, 16+ API routes).

6. WIREFLOWS

Excluded.

7. PROTOTYPE

Excluded — production deployed.


8. BACKEND SCHEMA

Database Tables

-- Core comments table
CREATE TABLE quest_comments (
id UUID PK, quest_id FK→quests ON DELETE CASCADE, node_id TEXT NOT NULL,
app_user_id FK→app_users ON DELETE SET NULL, -- NULL for guests
guest_invite_id FK→quest_invites ON DELETE SET NULL, -- NULL for authenticated
content TEXT NOT NULL, is_resolved BOOLEAN DEFAULT FALSE,
parent_comment_id FK→quest_comments ON DELETE SET NULL, -- Threading
created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ
);
-- Constraint: chk_author_exists: app_user_id IS NOT NULL OR guest_invite_id IS NOT NULL
 
-- Comment deletion audit log (trigger-based)
CREATE TABLE comment_deletion_log (
id UUID PK, comment_id UUID NOT NULL, quest_id UUID NOT NULL,
node_id TEXT NOT NULL, content_hash TEXT NOT NULL, -- SHA-256
deleted_by_user FK→app_users, deleted_by_role TEXT,
deleted_by_guest UUID, deletion_reason TEXT, deleted_at TIMESTAMPTZ
);
-- Trigger: log_comment_deletion() BEFORE DELETE on quest_comments (SECURITY DEFINER)
-- RLS: ADMIN/AGENCY only

RLS History

  • Initial RLS was service_role-only (migration 20260326)

  • Replaced by fine-grained per-user RLS (migration 202604140001):

    • SELECT: quest creators, admins, comment authors

    • INSERT: authenticated users + guests

    • UPDATE/DELETE: comment author or admin/agency


9. API ENDPOINTS (Core Commenting)

Authenticated Comments

GET /api/reviewer/comments?quest_id=&node_id=&page=&limit=
POST /api/reviewer/comments/create (content max 5000 chars, sanitized)
PATCH /api/reviewer/comments/[commentId] (resolve/unresolve)
DELETE /api/reviewer/comments/[commentId] (author or admin — triggers deletion log)

Bulk Operations

PATCH /api/reviewer/comments/bulk-resolve (CREATOR/ADMIN/AGENCY)
DELETE /api/reviewer/comments/bulk-delete (ADMIN/AGENCY only)
POST /api/reviewer/comments/export (CSV or JSON)

Analytics

GET /api/reviewer/analytics/node-feedback (per-node heatmap)
GET /api/reviewer/analytics/resolution (rate, avg time, oldest)
GET /api/reviewer/analytics/activity (per-reviewer stats)
GET /api/reviewer/analytics/personal (current user)

Schemas

Defined in lib/schemas/commenting.schema.ts (216 lines, core CRUD), commenting-bulk.schema.ts (59 lines), analytics.schema.ts (47 lines).


10. DATA REQUIREMENTS

Frontend Needs

  • CommentPanel.tsx (479 lines) + CommentThread.tsx (296 lines) + CommentForm.tsx (209 lines).

  • CommentingContext from components/context/CommentingContext.tsx providing selectedNodeId, isPanelOpen, questId via React context.

  • CommentNodeWrapper.tsx (61 lines) wraps canvas nodes.

  • Node display names via getNodeDisplayNames() from lib/utils/node-display-name.ts.

Caching Strategy

  • Comments fetched fresh on panel open/close (no stale cache).


11. PERFORMANCE CONSIDERATIONS

Database Optimization

  • Indexes on quest_comments(quest_id), (node_id), (parent_comment_id).

  • Batched user name lookups (single query for all comment authors).

API Response Time

Target: <300ms for comment list (<100 comments per quest typical).


12. SECURITY & AUTHORIZATION

Who can access this feature?

  • Creators: view + resolve comments on their quests.

  • Reviewers: full CRUD on claimed quests.

  • Admins/Agency: view all, delete any.

  • Guests: create only (token-based, see [7.2]).

RLS Policies

Fine-grained per-user RLS (migration 202604140001): SELECT for quest creators/admins/comment authors, INSERT for authenticated users + guests, UPDATE/DELETE for comment author or admin/agency. comment_deletion_log restricted to ADMIN/AGENCY only.

Content Moderation

  • sanitizeCommentContent() strips HTML/script tags before storage.

  • Content validation via Zod schemas in lib/schemas/commenting.schema.ts.


13. ERROR HANDLING

Error

Response

5000-char limit exceeded

400: "Comment exceeds maximum length"

Content sanitization triggered

Content cleaned silently (HTML tags stripped)

Non-author delete attempt

403: "Only the comment author can delete this"

Invalid node_id

400: Validation error


14. TESTING CHECKLIST

Happy Path
□ Create comment → appears in panel → creator notified (fire-and-forget)
□ Reply to comment → thread displayed with indentation
□ Resolve comment → marked resolved, resolution rate updated
□ Export as CSV/JSON → all comments included

Edge Cases
□ Empty comment → validation error
□ HTML injection → sanitized
□ Delete parent comment → replies survive (SET NULL on FK)
□ Non-author delete → 403
□ deletion_log trigger fires → content_hash + metadata logged


15. OPEN QUESTIONS

  • "Action Items (Feedback)" in ReviewSidebar is currently a placeholder — should pull real comment data.

  • CommentNotification component integration with CommentingContext.


16. OUT OF SCOPE (v1.1+)

  • Rich text formatting in comments.

  • @mentions within comments.

  • Comment reactions/upvotes.

  • Real-time (WebSocket) comment updates.


17. SUCCESS METRICS

  • Average comments per review: 5+.

  • Resolution rate: >80%.

  • All comment deletions logged via comment_deletion_log trigger.


18. DEPENDENCIES

This feature depends on:

  • [5.4] Reviewer Queue Backend — review pipeline.

  • [7.2] Guest Access & Invitations — guest comment auth.

  • [7.3] Notification System — fire-and-forget comment notifications.

  • [W1.5] Canvas Schema — node-level attachment.

  • [W1.4] ApiResponse — standardized responses.

These features depend on this:

  • Reviewer approval workflow (must resolve comments before approving).

  • Creator revision workflow (comments guide changes).


19. TIMELINE & OWNERSHIP

Sprint #12: Already deployed.
Backend: scorevi
Frontend: scorevi
Estimated Completion: Complete.


Document Version

1.0 - Initial version - 2026-06-29 08:23 UTC

1.1 - Added Document Version section and update author to have full name - 2026-06-29 08:48 UTC


Was this article helpful?