commit 678c66042402ca3618ea80278e59c5aed35f55e0
parent 3c9c22168ec41dc26315645d4b1f1b7611d12128
Author: triesap <tyson@radroots.org>
Date: Fri, 12 Jun 2026 16:16:26 -0700
events: add surface kind classifiers
- add explicit Home Events Market Map Profile farm-ops kind sets
- separate moderation reports and draft listings from public feed candidates
- keep NIP-65 relay lists under list classification instead of social feeds
- update social classifier tests and conformance vectors
Diffstat:
5 files changed, 337 insertions(+), 30 deletions(-)
diff --git a/crates/events/src/kinds.rs b/crates/events/src/kinds.rs
@@ -200,33 +200,108 @@ pub const KIND_JOB_RESULT_MIN: u32 = 6000;
pub const KIND_JOB_RESULT_MAX: u32 = 6999;
pub const KIND_JOB_FEEDBACK: u32 = 7000;
-pub const PUBLIC_SOCIAL_KINDS: [u32; 14] = [
+pub const HOME_FEED_CANDIDATE_KINDS: [u32; 9] = [
+ KIND_POST,
+ KIND_REPOST,
+ KIND_GENERIC_REPOST,
+ KIND_ARTICLE,
+ KIND_LISTING,
+ KIND_CALENDAR_DATE_EVENT,
+ KIND_CALENDAR_TIME_EVENT,
+ KIND_FARM,
+ KIND_PUBLIC_FILE_METADATA,
+];
+
+pub const EVENTS_CANDIDATE_KINDS: [u32; 4] = [
+ KIND_CALENDAR_DATE_EVENT,
+ KIND_CALENDAR_TIME_EVENT,
+ KIND_CALENDAR,
+ KIND_CALENDAR_EVENT_RSVP,
+];
+
+pub const MARKET_CANDIDATE_KINDS: [u32; 3] = [KIND_LISTING, KIND_FARM, KIND_PUBLIC_FILE_METADATA];
+
+pub const MAP_CANDIDATE_KINDS: [u32; 7] = [
+ KIND_FARM,
+ KIND_LISTING,
+ KIND_CALENDAR_DATE_EVENT,
+ KIND_CALENDAR_TIME_EVENT,
+ KIND_POST,
+ KIND_ARTICLE,
+ KIND_PUBLIC_FILE_METADATA,
+];
+
+pub const PROFILE_PUBLIC_CONTENT_KINDS: [u32; 8] = [
+ KIND_POST,
+ KIND_REPOST,
+ KIND_GENERIC_REPOST,
+ KIND_ARTICLE,
+ KIND_LISTING,
+ KIND_CALENDAR_DATE_EVENT,
+ KIND_CALENDAR_TIME_EVENT,
+ KIND_PUBLIC_FILE_METADATA,
+];
+
+pub const MODERATION_ADMIN_CANDIDATE_KINDS: [u32; 1] = [KIND_REPORT];
+
+pub const DRAFT_OWNER_CANDIDATE_KINDS: [u32; 1] = [KIND_LISTING_DRAFT];
+
+pub const NIP29_GROUP_KINDS: [u32; 13] = [
+ KIND_GROUP_METADATA,
+ KIND_GROUP_ADMINS,
+ KIND_GROUP_MEMBERS,
+ KIND_GROUP_ROLES,
+ KIND_GROUP_PUT_USER,
+ KIND_GROUP_REMOVE_USER,
+ KIND_GROUP_EDIT_METADATA,
+ KIND_GROUP_DELETE_EVENT,
+ KIND_GROUP_CREATE_GROUP,
+ KIND_GROUP_DELETE_GROUP,
+ KIND_GROUP_CREATE_INVITE,
+ KIND_GROUP_JOIN_REQUEST,
+ KIND_GROUP_LEAVE_REQUEST,
+];
+
+pub const PRIVATE_FARM_OPS_KINDS: [u32; 16] = [
+ KIND_FARM_WORKSPACE_MANIFEST,
+ KIND_FARM_CRDT_CHANGE,
+ KIND_FARM_FILE_METADATA,
+ KIND_GROUP_METADATA,
+ KIND_GROUP_ADMINS,
+ KIND_GROUP_MEMBERS,
+ KIND_GROUP_ROLES,
+ KIND_GROUP_PUT_USER,
+ KIND_GROUP_REMOVE_USER,
+ KIND_GROUP_EDIT_METADATA,
+ KIND_GROUP_DELETE_EVENT,
+ KIND_GROUP_CREATE_GROUP,
+ KIND_GROUP_DELETE_GROUP,
+ KIND_GROUP_CREATE_INVITE,
+ KIND_GROUP_JOIN_REQUEST,
+ KIND_GROUP_LEAVE_REQUEST,
+];
+
+pub const PUBLIC_SOCIAL_KINDS: [u32; 11] = [
KIND_POST,
KIND_REPOST,
KIND_REACTION,
KIND_GENERIC_REPOST,
KIND_PUBLIC_FILE_METADATA,
KIND_COMMENT,
- KIND_REPORT,
- KIND_LIST_READ_WRITE_RELAYS,
KIND_ARTICLE,
- KIND_LISTING_DRAFT,
KIND_CALENDAR_DATE_EVENT,
KIND_CALENDAR_TIME_EVENT,
KIND_CALENDAR,
KIND_CALENDAR_EVENT_RSVP,
];
-pub const UNAMBIGUOUS_PUBLIC_SOCIAL_KINDS: [u32; 13] = [
+pub const UNAMBIGUOUS_PUBLIC_SOCIAL_KINDS: [u32; 10] = [
KIND_POST,
KIND_REPOST,
KIND_REACTION,
KIND_GENERIC_REPOST,
KIND_COMMENT,
- KIND_REPORT,
- KIND_LIST_READ_WRITE_RELAYS,
KIND_ARTICLE,
- KIND_LISTING_DRAFT,
KIND_CALENDAR_DATE_EVENT,
KIND_CALENDAR_TIME_EVENT,
KIND_CALENDAR,
@@ -241,12 +316,9 @@ pub const MVP_SOCIAL_KINDS: [u32; 5] = [
KIND_CALENDAR_TIME_EVENT,
];
-pub const PRODUCTION_SOCIAL_KINDS: [u32; 7] = [
+pub const PRODUCTION_SOCIAL_KINDS: [u32; 4] = [
KIND_REPOST,
KIND_GENERIC_REPOST,
- KIND_REPORT,
- KIND_LIST_READ_WRITE_RELAYS,
- KIND_LISTING_DRAFT,
KIND_CALENDAR,
KIND_CALENDAR_EVENT_RSVP,
];
@@ -275,10 +347,7 @@ pub const fn is_unambiguous_public_social_kind(kind: u32) -> bool {
| KIND_REACTION
| KIND_GENERIC_REPOST
| KIND_COMMENT
- | KIND_REPORT
- | KIND_LIST_READ_WRITE_RELAYS
| KIND_ARTICLE
- | KIND_LISTING_DRAFT
| KIND_CALENDAR_DATE_EVENT
| KIND_CALENDAR_TIME_EVENT
| KIND_CALENDAR
@@ -307,17 +376,110 @@ pub const fn is_mvp_social_kind(kind: u32) -> bool {
pub const fn is_production_social_kind(kind: u32) -> bool {
matches!(
kind,
- KIND_REPOST
+ KIND_REPOST | KIND_GENERIC_REPOST | KIND_CALENDAR | KIND_CALENDAR_EVENT_RSVP
+ )
+}
+
+#[inline]
+pub const fn is_home_feed_candidate_kind(kind: u32) -> bool {
+ matches!(
+ kind,
+ KIND_POST
+ | KIND_REPOST
| KIND_GENERIC_REPOST
- | KIND_REPORT
- | KIND_LIST_READ_WRITE_RELAYS
- | KIND_LISTING_DRAFT
+ | KIND_ARTICLE
+ | KIND_LISTING
+ | KIND_CALENDAR_DATE_EVENT
+ | KIND_CALENDAR_TIME_EVENT
+ | KIND_FARM
+ | KIND_PUBLIC_FILE_METADATA
+ )
+}
+
+#[inline]
+pub const fn is_events_candidate_kind(kind: u32) -> bool {
+ matches!(
+ kind,
+ KIND_CALENDAR_DATE_EVENT
+ | KIND_CALENDAR_TIME_EVENT
| KIND_CALENDAR
| KIND_CALENDAR_EVENT_RSVP
)
}
#[inline]
+pub const fn is_market_candidate_kind(kind: u32) -> bool {
+ matches!(kind, KIND_LISTING | KIND_FARM | KIND_PUBLIC_FILE_METADATA)
+}
+
+#[inline]
+pub const fn is_map_candidate_kind(kind: u32) -> bool {
+ matches!(
+ kind,
+ KIND_FARM
+ | KIND_LISTING
+ | KIND_CALENDAR_DATE_EVENT
+ | KIND_CALENDAR_TIME_EVENT
+ | KIND_POST
+ | KIND_ARTICLE
+ | KIND_PUBLIC_FILE_METADATA
+ )
+}
+
+#[inline]
+pub const fn is_profile_public_content_kind(kind: u32) -> bool {
+ matches!(
+ kind,
+ KIND_POST
+ | KIND_REPOST
+ | KIND_GENERIC_REPOST
+ | KIND_ARTICLE
+ | KIND_LISTING
+ | KIND_CALENDAR_DATE_EVENT
+ | KIND_CALENDAR_TIME_EVENT
+ | KIND_PUBLIC_FILE_METADATA
+ )
+}
+
+#[inline]
+pub const fn is_moderation_admin_candidate_kind(kind: u32) -> bool {
+ kind == KIND_REPORT
+}
+
+#[inline]
+pub const fn is_draft_owner_candidate_kind(kind: u32) -> bool {
+ kind == KIND_LISTING_DRAFT
+}
+
+#[inline]
+pub const fn is_nip29_group_kind(kind: u32) -> bool {
+ matches!(
+ kind,
+ KIND_GROUP_METADATA
+ | KIND_GROUP_ADMINS
+ | KIND_GROUP_MEMBERS
+ | KIND_GROUP_ROLES
+ | KIND_GROUP_PUT_USER
+ | KIND_GROUP_REMOVE_USER
+ | KIND_GROUP_EDIT_METADATA
+ | KIND_GROUP_DELETE_EVENT
+ | KIND_GROUP_CREATE_GROUP
+ | KIND_GROUP_DELETE_GROUP
+ | KIND_GROUP_CREATE_INVITE
+ | KIND_GROUP_JOIN_REQUEST
+ | KIND_GROUP_LEAVE_REQUEST
+ )
+}
+
+#[inline]
+pub const fn is_private_farm_ops_kind(kind: u32) -> bool {
+ kind == KIND_FARM_WORKSPACE_MANIFEST
+ || kind == KIND_FARM_CRDT_CHANGE
+ || kind == KIND_FARM_FILE_METADATA
+ || is_nip29_group_kind(kind)
+}
+
+#[inline]
pub const fn is_trade_service_request_kind(kind: u32) -> bool {
matches!(
kind,
@@ -607,10 +769,10 @@ mod tests {
#[test]
fn classifies_public_social_kinds() {
- assert_eq!(PUBLIC_SOCIAL_KINDS.len(), 14);
- assert_eq!(UNAMBIGUOUS_PUBLIC_SOCIAL_KINDS.len(), 13);
+ assert_eq!(PUBLIC_SOCIAL_KINDS.len(), 11);
+ assert_eq!(UNAMBIGUOUS_PUBLIC_SOCIAL_KINDS.len(), 10);
assert_eq!(MVP_SOCIAL_KINDS.len(), 5);
- assert_eq!(PRODUCTION_SOCIAL_KINDS.len(), 7);
+ assert_eq!(PRODUCTION_SOCIAL_KINDS.len(), 4);
assert!(is_public_social_kind(KIND_POST));
assert!(is_public_social_kind(KIND_PUBLIC_FILE_METADATA));
@@ -621,17 +783,18 @@ mod tests {
assert!(is_public_social_kind(KIND_CALENDAR_TIME_EVENT));
assert!(is_public_social_kind(KIND_REPOST));
assert!(is_public_social_kind(KIND_GENERIC_REPOST));
- assert!(is_public_social_kind(KIND_REPORT));
assert!(is_public_social_kind(KIND_CALENDAR));
assert!(is_public_social_kind(KIND_CALENDAR_EVENT_RSVP));
- assert!(is_public_social_kind(KIND_LISTING_DRAFT));
- assert!(is_public_social_kind(KIND_LIST_READ_WRITE_RELAYS));
+ assert!(!is_public_social_kind(KIND_REPORT));
+ assert!(!is_public_social_kind(KIND_LISTING));
+ assert!(!is_public_social_kind(KIND_LISTING_DRAFT));
+ assert!(!is_public_social_kind(KIND_LIST_READ_WRITE_RELAYS));
assert!(!is_public_social_kind(KIND_FARM_CRDT_CHANGE));
assert!(!is_public_social_kind(KIND_FARM_WORKSPACE_MANIFEST));
assert!(is_mvp_social_kind(KIND_ARTICLE));
assert!(!is_mvp_social_kind(KIND_REPORT));
- assert!(is_production_social_kind(KIND_REPORT));
+ assert!(!is_production_social_kind(KIND_REPORT));
assert!(!is_production_social_kind(KIND_ARTICLE));
assert!(is_ambiguous_public_social_kind(KIND_PUBLIC_FILE_METADATA));
assert!(!is_unambiguous_public_social_kind(
@@ -641,6 +804,95 @@ mod tests {
}
#[test]
+ fn classifies_product_surface_candidate_kinds() {
+ assert_eq!(HOME_FEED_CANDIDATE_KINDS.len(), 9);
+ assert_eq!(EVENTS_CANDIDATE_KINDS.len(), 4);
+ assert_eq!(MARKET_CANDIDATE_KINDS.len(), 3);
+ assert_eq!(MAP_CANDIDATE_KINDS.len(), 7);
+ assert_eq!(PROFILE_PUBLIC_CONTENT_KINDS.len(), 8);
+ assert_eq!(MODERATION_ADMIN_CANDIDATE_KINDS.len(), 1);
+ assert_eq!(DRAFT_OWNER_CANDIDATE_KINDS.len(), 1);
+ assert_eq!(NIP29_GROUP_KINDS.len(), 13);
+ assert_eq!(PRIVATE_FARM_OPS_KINDS.len(), 16);
+
+ assert!(is_home_feed_candidate_kind(KIND_POST));
+ assert!(is_home_feed_candidate_kind(KIND_REPOST));
+ assert!(is_home_feed_candidate_kind(KIND_GENERIC_REPOST));
+ assert!(is_home_feed_candidate_kind(KIND_ARTICLE));
+ assert!(is_home_feed_candidate_kind(KIND_LISTING));
+ assert!(is_home_feed_candidate_kind(KIND_CALENDAR_DATE_EVENT));
+ assert!(is_home_feed_candidate_kind(KIND_CALENDAR_TIME_EVENT));
+ assert!(is_home_feed_candidate_kind(KIND_FARM));
+ assert!(is_home_feed_candidate_kind(KIND_PUBLIC_FILE_METADATA));
+ assert!(!is_home_feed_candidate_kind(KIND_LISTING_DRAFT));
+ assert!(!is_home_feed_candidate_kind(KIND_REPORT));
+ assert!(!is_home_feed_candidate_kind(KIND_FARM_CRDT_CHANGE));
+ assert!(!is_home_feed_candidate_kind(KIND_RELAY_AUTH));
+ assert!(!is_home_feed_candidate_kind(KIND_HTTP_AUTH));
+
+ assert!(is_events_candidate_kind(KIND_CALENDAR_DATE_EVENT));
+ assert!(is_events_candidate_kind(KIND_CALENDAR_TIME_EVENT));
+ assert!(is_events_candidate_kind(KIND_CALENDAR));
+ assert!(is_events_candidate_kind(KIND_CALENDAR_EVENT_RSVP));
+ assert!(!is_events_candidate_kind(KIND_POST));
+ assert!(!is_events_candidate_kind(KIND_FARM_CRDT_CHANGE));
+
+ assert!(is_market_candidate_kind(KIND_LISTING));
+ assert!(is_market_candidate_kind(KIND_FARM));
+ assert!(is_market_candidate_kind(KIND_PUBLIC_FILE_METADATA));
+ assert!(!is_market_candidate_kind(KIND_LISTING_DRAFT));
+ assert!(!is_market_candidate_kind(KIND_REPORT));
+
+ assert!(is_map_candidate_kind(KIND_FARM));
+ assert!(is_map_candidate_kind(KIND_LISTING));
+ assert!(is_map_candidate_kind(KIND_CALENDAR_DATE_EVENT));
+ assert!(is_map_candidate_kind(KIND_CALENDAR_TIME_EVENT));
+ assert!(is_map_candidate_kind(KIND_POST));
+ assert!(is_map_candidate_kind(KIND_ARTICLE));
+ assert!(is_map_candidate_kind(KIND_PUBLIC_FILE_METADATA));
+ assert!(!is_map_candidate_kind(KIND_LISTING_DRAFT));
+ assert!(!is_map_candidate_kind(KIND_REPORT));
+
+ assert!(is_profile_public_content_kind(KIND_POST));
+ assert!(is_profile_public_content_kind(KIND_REPOST));
+ assert!(is_profile_public_content_kind(KIND_GENERIC_REPOST));
+ assert!(is_profile_public_content_kind(KIND_ARTICLE));
+ assert!(is_profile_public_content_kind(KIND_LISTING));
+ assert!(is_profile_public_content_kind(KIND_CALENDAR_DATE_EVENT));
+ assert!(is_profile_public_content_kind(KIND_CALENDAR_TIME_EVENT));
+ assert!(is_profile_public_content_kind(KIND_PUBLIC_FILE_METADATA));
+ assert!(!is_profile_public_content_kind(KIND_LISTING_DRAFT));
+ assert!(!is_profile_public_content_kind(KIND_REPORT));
+
+ assert!(is_moderation_admin_candidate_kind(KIND_REPORT));
+ assert!(!is_moderation_admin_candidate_kind(KIND_POST));
+ assert!(is_draft_owner_candidate_kind(KIND_LISTING_DRAFT));
+ assert!(!is_draft_owner_candidate_kind(KIND_LISTING));
+
+ assert!(is_private_farm_ops_kind(KIND_FARM_WORKSPACE_MANIFEST));
+ assert!(is_private_farm_ops_kind(KIND_FARM_CRDT_CHANGE));
+ assert!(is_private_farm_ops_kind(KIND_FARM_FILE_METADATA));
+ assert!(is_nip29_group_kind(KIND_GROUP_METADATA));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_METADATA));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_ADMINS));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_MEMBERS));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_ROLES));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_PUT_USER));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_REMOVE_USER));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_EDIT_METADATA));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_DELETE_EVENT));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_CREATE_GROUP));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_DELETE_GROUP));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_CREATE_INVITE));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_JOIN_REQUEST));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_LEAVE_REQUEST));
+ assert!(!is_private_farm_ops_kind(KIND_RELAY_AUTH));
+ assert!(!is_private_farm_ops_kind(KIND_HTTP_AUTH));
+ assert!(!is_private_farm_ops_kind(KIND_REPORT));
+ assert!(!is_private_farm_ops_kind(KIND_LISTING_DRAFT));
+ }
+
+ #[test]
fn exposes_nip29_group_kind_constants() {
assert_eq!(KIND_GROUP_PUT_USER, 9000);
assert_eq!(KIND_GROUP_REMOVE_USER, 9001);
diff --git a/crates/events/src/list.rs b/crates/events/src/list.rs
@@ -18,7 +18,7 @@ pub struct RadrootsListEntry {
#[cfg(test)]
mod tests {
use super::*;
- use crate::kinds::{KIND_LIST_READ_WRITE_RELAYS, is_public_social_kind};
+ use crate::kinds::{KIND_LIST_READ_WRITE_RELAYS, is_nip51_standard_list_kind};
#[test]
fn generic_list_model_covers_nip65_relay_entries() {
@@ -39,6 +39,6 @@ mod tests {
assert_eq!(list.entries.len(), 2);
assert_eq!(list.entries[0].tag, "r");
assert_eq!(list.entries[0].values[1], "read");
- assert!(is_public_social_kind(KIND_LIST_READ_WRITE_RELAYS));
+ assert!(is_nip51_standard_list_kind(KIND_LIST_READ_WRITE_RELAYS));
}
}
diff --git a/crates/events_codec/tests/social_events.rs b/crates/events_codec/tests/social_events.rs
@@ -7,8 +7,10 @@ use radroots_events::{
file_metadata::RadrootsFileMetadata,
group::{RadrootsGroupEditableMetadata, RadrootsGroupMetadata},
kinds::{
- KIND_ARTICLE, KIND_FARM, KIND_FARM_CRDT_CHANGE, KIND_GROUP_METADATA, KIND_LISTING,
- KIND_POST, KIND_PUBLIC_FILE_METADATA, is_public_social_kind,
+ KIND_ARTICLE, KIND_FARM, KIND_FARM_CRDT_CHANGE, KIND_GROUP_METADATA, KIND_HTTP_AUTH,
+ KIND_LISTING, KIND_LISTING_DRAFT, KIND_POST, KIND_PUBLIC_FILE_METADATA, KIND_RELAY_AUTH,
+ KIND_REPORT, is_home_feed_candidate_kind, is_market_candidate_kind,
+ is_private_farm_ops_kind, is_public_social_kind,
},
social::RadrootsSocialMediaDimensions,
};
@@ -97,6 +99,16 @@ fn social_events_reject_private_farm_ops_semantics_in_public_codecs() {
assert!(is_public_social_kind(KIND_PUBLIC_FILE_METADATA));
assert!(!is_public_social_kind(KIND_FARM_CRDT_CHANGE));
assert!(!is_public_social_kind(KIND_LISTING));
+ assert!(is_home_feed_candidate_kind(KIND_LISTING));
+ assert!(is_home_feed_candidate_kind(KIND_FARM));
+ assert!(is_home_feed_candidate_kind(KIND_PUBLIC_FILE_METADATA));
+ assert!(!is_home_feed_candidate_kind(KIND_LISTING_DRAFT));
+ assert!(!is_home_feed_candidate_kind(KIND_REPORT));
+ assert!(!is_home_feed_candidate_kind(KIND_FARM_CRDT_CHANGE));
+ assert!(!is_home_feed_candidate_kind(KIND_RELAY_AUTH));
+ assert!(!is_home_feed_candidate_kind(KIND_HTTP_AUTH));
+ assert!(is_market_candidate_kind(KIND_LISTING));
+ assert!(!is_market_candidate_kind(KIND_LISTING_DRAFT));
}
#[test]
@@ -117,6 +129,8 @@ fn social_events_keep_nip29_groups_out_of_public_social_classification() {
let parts = group_metadata_to_wire_parts(&group).unwrap();
assert_eq!(parts.kind, KIND_GROUP_METADATA);
assert!(!is_public_social_kind(KIND_GROUP_METADATA));
+ assert!(!is_home_feed_candidate_kind(KIND_GROUP_METADATA));
+ assert!(is_private_farm_ops_kind(KIND_GROUP_METADATA));
assert_eq!(
group_metadata_from_event(parts.kind, &parts.tags, &parts.content)
.unwrap()
diff --git a/spec/conformance/vectors/social/upgraded_boundaries.v1.json b/spec/conformance/vectors/social/upgraded_boundaries.v1.json
@@ -222,8 +222,41 @@
"expected": {
"result": "ok",
"public_social_kind": false,
+ "home_feed_candidate_kind": false,
+ "market_candidate_kind": false,
+ "private_farm_ops_kind": true,
"group_infrastructure": true
}
+ },
+ {
+ "id": "social_surface_kind_sets_valid_010",
+ "kind": "social.surface_kinds.classification.valid",
+ "input": {
+ "kinds": {
+ "active_listing": 30402,
+ "listing_draft": 30403,
+ "report": 1984,
+ "farm_crdt_change": 78,
+ "relay_auth": 22242,
+ "http_auth": 27235
+ }
+ },
+ "expected": {
+ "result": "ok",
+ "home_feed_active_listing": true,
+ "home_feed_listing_draft": false,
+ "home_feed_report": false,
+ "home_feed_farm_crdt_change": false,
+ "home_feed_relay_auth": false,
+ "home_feed_http_auth": false,
+ "market_active_listing": true,
+ "market_listing_draft": false,
+ "moderation_report": true,
+ "draft_owner_listing_draft": true,
+ "private_farm_ops_farm_crdt_change": true,
+ "private_farm_ops_relay_auth": false,
+ "private_farm_ops_http_auth": false
+ }
}
]
}
diff --git a/spec/social-events.md b/spec/social-events.md
@@ -84,6 +84,14 @@ optional uppercase `D` tags for covered all-day dates. Calendar time events requ
uppercase `D` tag so timestamped events retain a deterministic calendar-date anchor across codecs and
language exports.
+Product routing uses surface-specific kind classifiers rather than a broad public-social set. Home,
+Events, Market, Map, and Profile public-content candidates are explicit. Active listing kind `30402`
+can appear in public product surfaces, but listing draft kind `30403` is limited to draft-owner
+contexts. Report kind `1984` is a moderation/admin candidate, not normal feed content. Relay and HTTP
+auth kinds are transient and excluded from durable social and farm-ops candidate sets. Private farm
+operations candidates include the farm workspace manifest, farm CRDT change envelope, farm file
+metadata, and the supported NIP-29 group event subset.
+
`RadrootsListingDraft` and `RadrootsRelayList` are not separate model types in the target contract.
Listing draft kind `30403` is represented through `RadrootsListing`, and NIP-51 standard and
list-set entries, including NIP-65 relay metadata kind `10002`, are represented through