A community based topic aggregation platform built on atproto

Merge branch 'refactor/lexicon-atproto-best-practices-comprehensive'

+2
internal/atproto/lexicon/social/coves/community/defs.json
···
"name": {
"type": "string",
"maxLength": 64,
+
"maxGraphemes": 64,
"description": "Short community name"
},
"displayName": {
···
"name": {
"type": "string",
"maxLength": 64,
+
"maxGraphemes": 64,
"description": "Short community name"
},
"displayName": {
+2 -2
internal/atproto/lexicon/social/coves/community/post/get.json
···
},
"blockedBy": {
"type": "string",
-
"enum": ["author", "community", "moderator"],
+
"knownValues": ["author", "community", "moderator"],
"description": "What caused the block: viewer blocked author, viewer blocked community, or post was removed by moderators"
},
"author": {
···
"properties": {
"vote": {
"type": "string",
-
"enum": ["up", "down"],
+
"knownValues": ["up", "down"],
"description": "Viewer's vote on this post"
},
"voteUri": {
+3 -3
internal/atproto/lexicon/social/coves/community/post/search.json
···
},
"type": {
"type": "string",
-
"enum": ["text", "image", "video", "article", "microblog"],
+
"knownValues": ["text", "image", "video", "article", "microblog"],
"description": "Filter by post type"
},
"tags": {
···
},
"sort": {
"type": "string",
-
"enum": ["relevance", "new", "top"],
+
"knownValues": ["relevance", "new", "top"],
"default": "relevance"
},
"timeframe": {
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
+
"knownValues": ["hour", "day", "week", "month", "year", "all"],
"default": "all"
},
"limit": {
+2
internal/atproto/lexicon/social/coves/community/post/update.json
···
"content": {
"type": "string",
"maxLength": 50000,
+
"maxGraphemes": 20000,
"description": "Updated content - main text for text posts, description for media, etc."
},
"facets": {
···
"editNote": {
"type": "string",
"maxLength": 300,
+
"maxGraphemes": 300,
"description": "Optional note explaining the edit"
}
}
+1
internal/atproto/lexicon/social/coves/community/profile.json
···
"name": {
"type": "string",
"maxLength": 64,
+
"maxGraphemes": 64,
"description": "Short community name (local part of handle)"
},
"displayName": {
+1 -1
internal/atproto/lexicon/social/coves/community/search.json
···
"type": "array",
"items": {
"type": "ref",
-
"ref": "social.coves.community.list#communityView"
+
"ref": "social.coves.community.defs#communityView"
}
},
"cursor": {
+3 -1
internal/atproto/lexicon/social/coves/embed/external.json
···
"title": {
"type": "string",
"maxLength": 500,
+
"maxGraphemes": 500,
"description": "Title of the linked content"
},
"description": {
"type": "string",
"maxLength": 1000,
+
"maxGraphemes": 1000,
"description": "Description or excerpt of the linked content"
},
"thumb": {
···
},
"embedType": {
"type": "string",
-
"enum": ["article", "image", "video-stream"],
+
"knownValues": ["article", "image", "video-stream"],
"description": "Type hint for special handling of known providers"
},
"provider": {
+1
internal/atproto/lexicon/social/coves/embed/images.json
···
"alt": {
"type": "string",
"maxLength": 1000,
+
"maxGraphemes": 1000,
"description": "Alt text for accessibility"
},
"aspectRatio": {
+1 -4
internal/atproto/lexicon/social/coves/embed/video.json
···
"alt": {
"type": "string",
"maxLength": 1000,
+
"maxGraphemes": 1000,
"description": "Alt text describing video content"
},
"duration": {
"type": "integer",
"minimum": 0,
"description": "Duration in seconds"
-
},
-
"aspectRatio": {
-
"type": "ref",
-
"ref": "social.coves.embed.image#aspectRatio"
}
}
}
-32
internal/atproto/lexicon/social/coves/federation/post.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.federation.post",
-
"defs": {
-
"main": {
-
"type": "object",
-
"description": "Reference to original federated post",
-
"required": ["platform", "uri"],
-
"properties": {
-
"platform": {
-
"type": "string",
-
"knownValues": ["bluesky", "lemmy", "atproto"],
-
"description": "Platform the post originated from"
-
},
-
"uri": {
-
"type": "string",
-
"format": "uri",
-
"description": "Original URI of the post (at:// URI for atproto platforms)"
-
},
-
"id": {
-
"type": "string",
-
"description": "Platform-specific post ID"
-
},
-
"originalCreatedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "Timestamp when originally posted on source platform"
-
}
-
}
-
}
-
}
-
}
+5 -5
internal/atproto/lexicon/social/coves/feed/getAll.json
···
"properties": {
"sort": {
"type": "string",
-
"enum": ["hot", "top", "new"],
+
"knownValues": ["hot", "top", "new"],
"default": "hot",
"description": "Sort order for global feed"
},
"postType": {
"type": "string",
-
"enum": ["text", "article", "image", "video", "microblog"],
+
"knownValues": ["text", "article", "image", "video", "microblog"],
"description": "Filter by a single post type (computed from embed structure)"
},
"postTypes": {
"type": "array",
"items": {
"type": "string",
-
"enum": ["text", "article", "image", "video", "microblog"]
+
"knownValues": ["text", "article", "image", "video", "microblog"]
},
"description": "Filter by multiple post types (computed from embed structure)"
},
"timeframe": {
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
+
"knownValues": ["hour", "day", "week", "month", "year", "all"],
"default": "day",
"description": "Timeframe for top sorting (only applies when sort=top)"
},
···
"type": "array",
"items": {
"type": "ref",
-
"ref": "social.coves.feed.getTimeline#feedViewPost"
+
"ref": "social.coves.feed.defs#feedViewPost"
}
},
"cursor": {
+3 -3
internal/atproto/lexicon/social/coves/feed/getCommunity.json
···
},
"sort": {
"type": "string",
-
"enum": ["hot", "top", "new"],
+
"knownValues": ["hot", "top", "new"],
"default": "hot",
"description": "Sort order for community feed"
},
"timeframe": {
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
+
"knownValues": ["hour", "day", "week", "month", "year", "all"],
"default": "day",
"description": "Timeframe for top sorting (only applies when sort=top)"
},
···
"type": "array",
"items": {
"type": "ref",
-
"ref": "social.coves.feed.getTimeline#feedViewPost"
+
"ref": "social.coves.feed.defs#feedViewPost"
}
},
"cursor": {
+2 -2
internal/atproto/lexicon/social/coves/feed/getDiscover.json
···
"properties": {
"sort": {
"type": "string",
-
"enum": ["hot", "top", "new"],
+
"knownValues": ["hot", "top", "new"],
"default": "hot",
"description": "Sort order for discover feed"
},
"timeframe": {
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
+
"knownValues": ["hour", "day", "week", "month", "year", "all"],
"default": "day",
"description": "Timeframe for top sorting (only applies when sort=top)"
},
+2 -2
internal/atproto/lexicon/social/coves/feed/getTimeline.json
···
"properties": {
"sort": {
"type": "string",
-
"enum": ["hot", "top", "new"],
+
"knownValues": ["hot", "top", "new"],
"default": "hot",
"description": "Sort order for timeline feed"
},
"timeframe": {
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
+
"knownValues": ["hour", "day", "week", "month", "year", "all"],
"default": "day",
"description": "Timeframe for top sorting (only applies when sort=top)"
},
-31
internal/atproto/lexicon/social/coves/interaction/share.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.interaction.share",
-
"defs": {
-
"main": {
-
"type": "record",
-
"description": "Sharing a post to another community or platform",
-
"key": "tid",
-
"record": {
-
"type": "object",
-
"required": ["subject", "createdAt"],
-
"properties": {
-
"subject": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post being shared"
-
},
-
"toCommunity": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "Community being shared to (if applicable)"
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
}
-
}
-
}
-33
internal/atproto/lexicon/social/coves/interaction/tag.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.interaction.tag",
-
"defs": {
-
"main": {
-
"type": "record",
-
"description": "A tag applied to a post or comment",
-
"key": "tid",
-
"record": {
-
"type": "object",
-
"required": ["subject", "tag", "createdAt"],
-
"properties": {
-
"subject": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post or comment being tagged"
-
},
-
"tag": {
-
"type": "string",
-
"minLength": 1,
-
"maxLength": 50,
-
"knownValues": ["helpful", "insightful", "spam", "hostile", "offtopic", "misleading"],
-
"description": "Predefined tag or custom community tag"
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
}
-
}
-
}
+13
scripts/validate-schemas.sh
···
+
#!/bin/bash
+
# Validate all lexicon schemas and test data
+
+
set -e
+
+
echo "🔍 Validating Coves lexicon schemas..."
+
echo ""
+
+
# Run the Go validation tool
+
go run ./cmd/validate-lexicon/main.go
+
+
echo ""
+
echo "✅ Schema validation complete!"
-9
tests/lexicon-test-data/community/moderator-invalid-permissions.json
···
-
{
-
"$type": "social.coves.community.moderator",
-
"user": "did:plc:moderator123",
-
"community": "did:plc:community123",
-
"role": "moderator",
-
"permissions": ["remove_posts", "invalid-permission"],
-
"createdAt": "2024-06-15T10:00:00Z",
-
"createdBy": "did:plc:owner123"
-
}
-5
tests/lexicon-test-data/interaction/share-valid-no-community.json
···
-
{
-
"$type": "social.coves.interaction.share",
-
"subject": "at://did:plc:originalauthor/social.coves.post.record/3k7a3dmb5bk2c",
-
"createdAt": "2025-01-09T17:00:00Z"
-
}
-6
tests/lexicon-test-data/interaction/share-valid.json
···
-
{
-
"$type": "social.coves.interaction.share",
-
"subject": "at://did:plc:originalauthor/social.coves.post.record/3k7a3dmb5bk2c",
-
"community": "did:plc:targetcommunity",
-
"createdAt": "2025-01-09T17:00:00Z"
-
}
-6
tests/lexicon-test-data/interaction/tag-invalid-empty.json
···
-
{
-
"$type": "social.coves.interaction.tag",
-
"subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c",
-
"tag": "",
-
"createdAt": "2025-01-09T17:15:00Z"
-
}
-6
tests/lexicon-test-data/interaction/tag-valid-custom.json
···
-
{
-
"$type": "social.coves.interaction.tag",
-
"subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c",
-
"tag": "beginner-friendly",
-
"createdAt": "2025-01-09T17:15:00Z"
-
}
-6
tests/lexicon-test-data/interaction/tag-valid-known.json
···
-
{
-
"$type": "social.coves.interaction.tag",
-
"subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c",
-
"tag": "nsfw",
-
"createdAt": "2025-01-09T17:15:00Z"
-
}
+1 -1
tests/lexicon-test-data/moderation/tribunal-vote-valid.json
···
{
"$type": "social.coves.moderation.tribunalVote",
"tribunal": "at://did:plc:community123/social.coves.moderation.tribunal/3k7a3dmb5bk2c",
-
"subject": "at://$1/social.coves.community.post/3k7a2clb4bj2b",
+
"subject": "at://did:plc:testuser123/social.coves.community.post/3k7a2clb4bj2b",
"decision": "remove",
"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.",
"precedents": [
-11
tests/lexicon-test-data/post/post-invalid-enum-type.json
···
-
{
-
"$type": "social.coves.community.post",
-
"community": "did:plc:programming123",
-
"author": "did:plc:testauthor123",
-
"postType": "invalid-type",
-
"title": "This has an invalid post type",
-
"content": "The postType field is not defined in the schema and should be rejected",
-
"tags": [],
-
"langs": ["en"],
-
"createdAt": "2025-01-09T14:30:00Z"
-
}