···1-Project:
2-You are a distinguished developer helping build Coves, a forum like atProto social media platform (think reddit / lemmy).
00000000034-Human & LLM Readability Guidelines:
5-- Clear Module Boundaries: Each feature is a self-contained module with explicit interfaces
6- Descriptive Naming: Use full words over abbreviations (e.g., CommunityGovernance not CommGov)
7-- Structured Documentation: Each module includes purpose, dependencies, and example usage
8-- Consistent Patterns: RESTful APIs, standard error handling, predictable data structures
9-- Context-Rich Comments: Explain "why" not just "what" at decision points
00000000000000000000000000000000000001011-Core Principles:
12-- When in doubt, choose the simpler implementation
13-- Features are the enemy of shipping
14-- A working tool today beats a perfect tool tomorrow
01516-Utilize existing tech stack
17-- Before attempting to use an external tool, ensure it cannot be done via the current stack:
18-- Go Chi (Web framework)
19-- DB: PostgreSQL
20-- atProto for federation & user identities
2122-## atProto Guidelines
00002324-For comprehensive AT Protocol implementation details, see [ATPROTO_GUIDE.md](./ATPROTO_GUIDE.md).
0002526-Key principles:
27-- Utilize Bluesky's Indigo packages before building custom atProto functionality
28-- Everything is XRPC - no separate REST API layer needed
29-- Follow the two-database pattern: Repository (CAR files) and AppView (PostgreSQL)
30-- Design for federation and data portability from the start
3132-# Architecture Guidelines
3334-## Required Layered Architecture
35-Follow this strict separation of concerns:
36-```
37-Handler (XRPC) → Service (Business Logic) → Repository (Data Access) → Database
38-```
39-- Handlers: XRPC request/response only
40-- Services: Business logic, uses both write/read repos
41-- Write Repos: CAR store operations
42-- Read Repos: AppView queries
4304445-## Directory Structure
4647-For a detailed project structure with file-level details and implementation status, see [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md).
000004849-The project follows a layered architecture with clear separation between:
50-- **XRPC handlers** - atProto API layer
51- - Only handle XRPC concerns: parsing requests, formatting responses
52- - Delegate all business logic to services
53- - No direct database access
54-- **Core business logic** - Domain services and models
55- - Contains all business logic
56- - Orchestrates between write and read repositories
57- - Manages transactions and complex operations
58-- **Data repositories** - Split between CAR store writes and AppView reads
59- - **Write Repositories** (`internal/atproto/carstore/*_write_repo.go`)
60- - Modify CAR files (source of truth)
61-- **Read Repositories** (`db/appview/*_read_repo.go`)
62- - Query denormalized PostgreSQL tables
63- - Optimized for performance
6465-## Strict Prohibitions
66-- **NEVER** put SQL queries in handlers
67-- **NEVER** import database packages in handlers
68-- **NEVER** pass *sql.DB directly to handlers
69-- **NEVER** mix business logic with XRPC concerns
70-- **NEVER** bypass the service layer
7172-## Testing Requirements
73-- Services must be easily mockable (use interfaces)
74-- Integration tests should test the full stack
75-- Unit tests should test individual layers in isolation
7677-Test File Naming:
78-- Unit tests: `[file]_test.go` in same directory
79-- Integration tests: `[feature]_integration_test.go` in tests/ directory
8081-## Claude Code Instructions
008283-### Code Generation Patterns
84-When creating new features:
85-1. Generate interface first in core/[domain]/
86-2. Generate test file with failing tests
87-3. Generate implementation to pass tests
88-4. Generate handler with tests
89-5. Update routes in xrpc/routes/
9091-### Refactoring Checklist
92-Before considering a feature complete:
93-- All tests pass
94-- No SQL in handlers
95-- Services use interfaces only
96-- Error handling follows patterns
97-- API documented with examples
9899-## Database Migrations
100-- Use golang-goose for version control
101-- Migrations in db/migrations/
102-- Never modify existing migrations
103-- Always provide rollback migrations
104105-## Dependency Injection
106-- Use constructor functions for all components
107-- Pass interfaces, not concrete types
108-- Wire dependencies in main.go or cmd/server/main.go
109110-Example dependency wiring:
111-```go
112-// main.go
113-userWriteRepo := carstore.NewUserWriteRepository(carStore)
114-userReadRepo := appview.NewUserReadRepository(db)
115-userService := users.NewUserService(userWriteRepo, userReadRepo)
116-userHandler := xrpc.NewUserHandler(userService)
117-```
118119-## Error Handling
120-- Define custom error types in core/errors/
121-- Use error wrapping with context: fmt.Errorf("service: %w", err)
122-- Services return domain errors, handlers translate to HTTP status codes
123-- Never expose internal error details in API responses
124125-### Context7 Usage Guidelines:
126-- Always check Context7 for best practices before implementing external integrations and packages
127-- Use Context7 to understand proper error handling patterns for specific libraries
128-- Reference Context7 for testing patterns with external dependencies
129-- Consult Context7 for proper configuration patterns
130131-## XRPC Implementation
0000132133-For detailed XRPC patterns and Lexicon examples, see [ATPROTO_GUIDE.md](./ATPROTO_GUIDE.md#xrpc).
134135-### Key Points
136-- All client interactions go through XRPC endpoints
137-- Handlers validate against Lexicon schemas automatically
138-- Queries are read-only, procedures modify repositories
139-- Every endpoint must have a corresponding Lexicon definition
140141-Key note: we are pre-production, we do not need migration strategies, feel free to tear down and rebuild, however ensure to erase any unneeded data structures or code.
···1+# CLAUDE-BUILD.md
2+3+Project: Coves Builder You are a distinguished developer actively building Coves, a forum-like atProto social media platform. Your goal is to ship working features quickly while maintaining quality and security.
4+5+## Builder Mindset
6+7+- Ship working code today, refactor tomorrow
8+- Security is built-in, not bolted-on
9+- Test-driven: write the test, then make it pass
10+- When stuck, check Context7 for patterns and examples
11+- ASK QUESTIONS if you need context surrounding the product DONT ASSUME
1213+#### Human & LLM Readability Guidelines:
014- Descriptive Naming: Use full words over abbreviations (e.g., CommunityGovernance not CommGov)
15+16+## Build Process
17+18+### Phase 1: Planning (Before Writing Code)
19+20+**ALWAYS START WITH:**
21+22+- [ ] Identify which atProto patterns apply (check ATPROTO_GUIDE.md or context7 https://context7.com/bluesky-social/atproto)
23+- [ ] Check if Indigo (also in context7) packages already solve this: https://context7.com/bluesky-social/indigo
24+- [ ] Define the XRPC interface first
25+- [ ] Write the Lexicon schema
26+- [ ] Plan the data flow: CAR store → AppView
27+ - [ ] - Follow the two-database pattern: Repository (CAR files)(PostgreSQL for metadata) and AppView (PostgreSQL)
28+- [ ] **Identify auth requirements and data sensitivity**
29+30+### Phase 2: Test-First Implementation
31+32+**BUILD ORDER:**
33+34+1. **Domain Model** (`core/[domain]/[domain].go`)
35+36+ - Start with the simplest struct
37+ - Add validation methods
38+ - Define error types
39+ - **Add input validation from the start**
40+2. **Repository Interfaces** (`core/[domain]/repository.go`)
41+42+ ```go
43+ type CommunityWriteRepository interface {
44+ Create(ctx context.Context, community *Community) error
45+ Update(ctx context.Context, community *Community) error
46+ }
47+48+ type CommunityReadRepository interface {
49+ GetByID(ctx context.Context, id string) (*Community, error)
50+ List(ctx context.Context, limit, offset int) ([]*Community, error)
51+ }
52+ ```
53+54+3. **Service Tests** (`core/[domain]/service_test.go`)
5556+ - Write failing tests for happy path
57+ - **Add tests for invalid inputs**
58+ - **Add tests for unauthorized access**
59+ - Mock repositories
60+4. **Service Implementation** (`core/[domain]/service.go`)
6162+ - Implement to pass tests
63+ - **Validate all inputs before processing**
64+ - **Check permissions before operations**
65+ - Handle transactions
66+5. **Repository Implementations**
6768+ - **Always use parameterized queries**
69+ - **Never concatenate user input into queries**
70+ - Write repo: `internal/atproto/carstore/[domain]_write_repo.go`
71+ - Read repo: `db/appview/[domain]_read_repo.go`
72+6. **XRPC Handler** (`xrpc/handlers/[domain]_handler.go`)
7374+ - **Verify auth tokens/DIDs**
75+ - Parse XRPC request
76+ - Call service
77+ - **Sanitize errors before responding**
7879+### Phase 3: Integration
00008081+**WIRE IT UP:**
8283+- [ ] Add to dependency injection in main.go
84+- [ ] Register XRPC routes with proper auth middleware
85+- [ ] Create migration if needed
86+- [ ] Write integration test including auth flows
000008788+## Security-First Building
8990+### Every Feature MUST:
9192+- [ ] **Validate all inputs** at the handler level
93+- [ ] **Use parameterized queries** (never string concatenation)
94+- [ ] **Check authorization** before any operation
95+- [ ] **Limit resource access** (pagination, rate limits)
96+- [ ] **Log security events** (failed auth, invalid inputs)
97+- [ ] **Never log sensitive data** (passwords, tokens, PII)
9899+### Red Flags to Avoid:
00000000000000100101+- `fmt.Sprintf` in SQL queries → Use parameterized queries
102+- Missing `context.Context` → Need it for timeouts/cancellation
103+- No input validation → Add it immediately
104+- Error messages with internal details → Wrap errors properly
105+- Unbounded queries → Add limits/pagination
0106107+## Quick Decision Guide
000108109+### "Should I use X?"
00110111+1. Does Indigo have it? → Use it
112+2. Can PostgreSQL + Go do it securely? → Build it simple
113+3. Requires external dependency? → Check Context7 first
114115+### "How should I structure this?"
000000116117+1. One domain, one package
118+2. Interfaces for testability
119+3. Services coordinate repos
120+4. Handlers only handle XRPC
000121122+## Pre-Production Advantages
0000123124+Since we're pre-production:
000125126+- **Break things**: Delete and rebuild rather than complex migrations
127+- **Experiment**: Try approaches, keep what works
128+- **Simplify**: Remove unused code aggressively
129+- **But never compromise security basics**
0000130131+## Success Metrics
0000132133+Your code is ready when:
0000134135+- [ ] Tests pass (including security tests)
136+- [ ] Follows atProto patterns
137+- [ ] No security checklist items missed
138+- [ ] Handles errors gracefully
139+- [ ] Works end-to-end with auth
140141+## Quick Checks Before Committing
142143+1. **Will it work?** (Integration test proves it)
144+2. 1. **Is it secure?** (Auth, validation, parameterized queries)
145+3. **Is it simple?** (Could you explain to a junior?)
146+4. **Is it complete?** (Test, implementation, documentation)
0147148+Remember: We're building a working product. Perfect is the enemy of shipped.
+39-2
cmd/validate-lexicon/main.go
···1package main
23import (
04 "encoding/json"
5 "flag"
6 "fmt"
···270 return nil
271 }
272273- // Parse JSON data
274 var recordData map[string]interface{}
275- if err := json.Unmarshal(data, &recordData); err != nil {
00276 validationErrors = append(validationErrors, fmt.Sprintf("Failed to parse JSON in %s: %v", path, err))
277 return nil
278 }
000279280 // Extract $type field
281 recordType, ok := recordData["$type"].(string)
···438439 return nil
440}
0000000000000000000000000000000
···1package main
23import (
4+ "bytes"
5 "encoding/json"
6 "flag"
7 "fmt"
···271 return nil
272 }
273274+ // Parse JSON data using Decoder to handle numbers properly
275 var recordData map[string]interface{}
276+ decoder := json.NewDecoder(bytes.NewReader(data))
277+ decoder.UseNumber() // This preserves numbers as json.Number instead of float64
278+ if err := decoder.Decode(&recordData); err != nil {
279 validationErrors = append(validationErrors, fmt.Sprintf("Failed to parse JSON in %s: %v", path, err))
280 return nil
281 }
282+283+ // Convert json.Number values to appropriate types
284+ recordData = convertNumbers(recordData).(map[string]interface{})
285286 // Extract $type field
287 recordType, ok := recordData["$type"].(string)
···444445 return nil
446}
447+448+// convertNumbers recursively converts json.Number values to int64 or float64
449+func convertNumbers(v interface{}) interface{} {
450+ switch vv := v.(type) {
451+ case map[string]interface{}:
452+ result := make(map[string]interface{})
453+ for k, val := range vv {
454+ result[k] = convertNumbers(val)
455+ }
456+ return result
457+ case []interface{}:
458+ result := make([]interface{}, len(vv))
459+ for i, val := range vv {
460+ result[i] = convertNumbers(val)
461+ }
462+ return result
463+ case json.Number:
464+ // Try to convert to int64 first
465+ if i, err := vv.Int64(); err == nil {
466+ return i
467+ }
468+ // If that fails, convert to float64
469+ if f, err := vv.Float64(); err == nil {
470+ return f
471+ }
472+ // If both fail, return as string
473+ return vv.String()
474+ default:
475+ return v
476+ }
477+}
···1+{
2+ "$type": "social.coves.community.wiki",
3+ "slug": "this-slug-is-way-too-long-and-exceeds-the-maximum-allowed-length-of-128-characters-which-should-trigger-a-validation-error-when-we-run-the-test",
4+ "title": "Invalid Wiki Page",
5+ "content": "This wiki page has a slug that exceeds the maximum length.",
6+ "createdAt": "2024-01-01T00:00:00Z"
7+}
+13
tests/lexicon-test-data/community/wiki-valid.json
···0000000000000
···1+{
2+ "$type": "social.coves.community.wiki",
3+ "slug": "getting-started",
4+ "title": "Getting Started with Our Community",
5+ "content": "# Welcome to the Programming Community\n\nThis guide will help you get started with our community.\n\n## Rules\nPlease read our community rules before posting.\n\n## Resources\n- [FAQ](/wiki/faq)\n- [Posting Guidelines](/wiki/posting-guidelines)\n- [Code of Conduct](/wiki/code-of-conduct)",
6+ "author": "did:plc:moderator123",
7+ "editors": ["did:plc:editor1", "did:plc:editor2"],
8+ "isIndex": false,
9+ "createdAt": "2024-01-01T00:00:00Z",
10+ "updatedAt": "2025-01-09T15:00:00Z",
11+ "revision": 5,
12+ "tags": ["meta", "help", "guide"]
13+}
···1+{
2+ "$type": "social.coves.moderation.ruleProposal",
3+ "community": "did:plc:programmingcommunity",
4+ "proposalType": "addRule",
5+ "title": "No AI-generated content without disclosure",
6+ "description": "All AI-generated code or content must be clearly marked as such. This helps maintain transparency and allows community members to make informed decisions about the content they consume.",
7+ "proposalData": {
8+ "ruleTitle": "Disclose AI-generated content",
9+ "ruleDescription": "All posts containing AI-generated code or content must include a clear disclosure statement"
10+ },
11+ "requiredVotes": 100,
12+ "createdAt": "2025-01-09T17:00:00Z"
13+}
···1+{
2+ "$type": "social.coves.moderation.tribunalVote",
3+ "tribunal": "at://did:plc:community123/social.coves.moderation.tribunal/3k7a3dmb5bk2c",
4+ "subject": "at://did:plc:spammer123/social.coves.post.record/3k7a2clb4bj2b",
5+ "decision": "remove",
6+ "reasoning": "The moderator's action was justified based on clear violation of Rule 2 (No Spam). The user posted the same promotional content across multiple communities within a short timeframe.",
7+ "precedents": [
8+ "at://did:plc:community123/social.coves.moderation.case/3k6z2cla4aj1a",
9+ "at://did:plc:community456/social.coves.moderation.case/3k6y1bkz3zi0z"
10+ ],
11+ "dissenting": false,
12+ "createdAt": "2025-01-09T18:00:00Z"
13+}