code
Clone this repository
https://tangled.org/bretton.dev/coves
git@knot.bretton.dev:bretton.dev/coves
For self-hosted knots, clone URLs may differ based on your setup.
Implement HTTP layer for GET /xrpc/social.coves.community.comment.getComments:
get_comments.go (168 lines):
- GetCommentsHandler: Main XRPC endpoint handler
- Parses query parameters (post, sort, depth, limit, cursor, timeframe)
- Validates inputs with clear error messages
- Extracts viewer DID from auth context
- Returns JSON matching lexicon output schema
- Comprehensive validation:
- Required: post (AT-URI format)
- Bounds: depth (0-100), limit (1-100)
- Enums: sort (hot/top/new), timeframe (hour/day/week/...)
- Business rules: timeframe only valid with sort=top
errors.go (45 lines):
- writeError: Standardized JSON error responses
- handleServiceError: Maps domain errors to HTTP status codes
- 404: IsNotFound
- 400: IsValidationError
- 500: Unexpected errors (logged)
- Never leaks internal error details
middleware.go (22 lines):
- OptionalAuthMiddleware: Wraps existing auth middleware
- Extracts viewer DID for personalized responses
- Gracefully degrades to anonymous (never rejects)
service_adapter.go (40 lines):
- Bridges handler layer (http.Request) and service layer (context.Context)
- Adapter pattern for clean separation of concerns
Security:
- All inputs validated at handler boundary
- Resource limits enforced
- Auth optional (supports public read)
- Error messages sanitized
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add service layer orchestrating comment queries and thread assembly:
comment_service.go (285 lines):
- GetComments: Main query method with validation and pagination
- buildThreadViews: Recursively constructs comment trees
- Iterative loading strategy (loads 5 replies per level)
- Respects depth limit (default 10, max 100)
- Sets HasMore flag for pagination hints
- buildCommentView: Converts entities to API views
- Hydrates author from CommenterHandle
- Builds stats (upvotes, downvotes, score, replyCount)
- Creates post/parent references with CIDs
- Stub viewer state (Phase 2B)
- validateGetCommentsRequest: Input validation with defaults
view_models.go (150 lines):
- CommentView: Complete comment with author, stats, viewer state
- ThreadViewComment: Recursive wrapper for nested replies
- Supporting types matching lexicon definitions
- Follows existing patterns from posts.AuthorView
Changes to existing files:
- comment.go: Add CommenterHandle field (hydrated at query time)
- errors.go: Add IsValidationError helper for handler error mapping
Design decisions:
- Empty slices instead of nil (JSON serialization)
- Iterative loading prevents N+1 query explosion
- Soft-deleted comments filtered out
- Post/user integration stubbed (Phase 2C)
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement database layer for comment queries with Lemmy hot ranking:
New repository methods:
- ListByParentWithHotRank: Query with hot/top/new sorting + pagination
- Hot: log(greatest(2, score + 2)) / power(time_decay, 1.8)
- Top: Score-based with optional timeframe filter
- New: Chronological ordering
- Cursor-based pagination with composite keys
- GetByURIsBatch: Batch fetch comments by URIs (prevents N+1 queries)
- GetVoteStateForComments: Fetch viewer votes (Phase 2B ready)
Key features:
- Hydrates author handle via JOIN with users table
- Supports timeframe filters (hour/day/week/month/year/all)
- Encodes cursors with hot_rank|score|created_at|uri
- All queries use parameterized arguments (SQL injection safe)
Formula prevents brigading:
- greatest(2, score + 2) ensures log never goes negative
- Heavily downvoted comments bounded at log(2)
- Power of 1.8 for faster decay than posts (1.5)
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add lexicon definitions for comment query API following Bluesky patterns:
- social.coves.community.comment.defs: Shared view definitions
- commentView: Base view for single comment with stats and viewer state
- threadViewComment: Recursive wrapper for threaded replies
- Supporting types: commentStats, commentViewerState, commentRef, etc.
- social.coves.community.comment.getComments: Query endpoint
- Parameters: post (required), sort, depth, limit, cursor, timeframe
- Returns threaded comments with nested replies up to depth limit
- Supports hot/top/new sorting with Lemmy-style hot ranking
Follows atproto best practices:
- Composition pattern (threadView wraps baseView)
- Union types for error states (notFound, blocked)
- Open unions for future extensibility
- Strong references with CID version pinning
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document P1 issue discovered during comment system implementation:
when comments arrive before their parent post (cross-repo Jetstream ordering),
the post's comment_count is never reconciled.
Issue details:
- Comment consumer updates post counts when processing events
- If comment arrives BEFORE post is indexed, update returns 0 rows
- When post consumer later indexes the post, it sets comment_count = 0
- NO reconciliation logic to count pre-existing comments
Solution: Post consumer must implement same reconciliation pattern as
comment consumer (COUNT subquery after insert).
Related: Comment reply_count reconciliation was fixed in comment system
implementation (2025-11-04).
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Initialize comment repository and Jetstream consumer at server startup.
Consumer runs in background goroutine, indexing comment events from
atProto firehose to PostgreSQL AppView.
Consumer lifecycle:
- Start on server init
- Graceful shutdown on SIGINT/SIGTERM
- Automatic reconnection on connection loss
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>