lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 6519051e4f6cd9f52c16c5ee2427efdcd6a7dde6
parent 582a37b43b56f20e51f0366652b93a67fbfc451c
Author: triesap <tyson@radroots.org>
Date:   Mon,  6 Jul 2026 01:43:32 +0000

events: make wiki article titles optional

Diffstat:
Mcrates/events/src/knowledge.rs | 33++++++++++++++++++++++++++++-----
Mcrates/events_codec/src/knowledge/decode.rs | 2+-
Mcrates/events_codec/src/knowledge/encode.rs | 2+-
Mcrates/events_codec/tests/knowledge.rs | 19+++++++++++++++++--
Mcrates/test_fixtures/src/knowledge.rs | 2+-
5 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/crates/events/src/knowledge.rs b/crates/events/src/knowledge.rs @@ -295,7 +295,9 @@ pub fn validate_wiki_article( validate_wiki_d_tag(article.d_tag.as_str()) .map(|_| ()) .map_err(|_| RadrootsKnowledgeValidationError::InvalidField("d_tag"))?; - require_non_empty(article.title.as_str(), "title")?; + if let Some(title) = article.title.as_deref() { + require_non_empty(title, "title")?; + } require_non_empty(article.content_djot.as_str(), "content_djot")?; validate_event_refs(&article.references, "references")?; for version_ref in &article.forked_from { @@ -550,7 +552,7 @@ pub struct RadrootsRightsAssertion { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsWikiArticle { pub d_tag: String, - pub title: String, + pub title: Option<String>, pub content_djot: String, pub summary: Option<String>, pub topics: Vec<String>, @@ -1138,7 +1140,7 @@ mod tests { fn models_wiki_article_as_djot_payload() { let article = RadrootsWikiArticle { d_tag: "soil-health".to_string(), - title: "Soil health".to_string(), + title: Some("Soil health".to_string()), content_djot: "# Soil health".to_string(), summary: Some("Living soil basics".to_string()), topics: vec!["soil".to_string()], @@ -1217,7 +1219,7 @@ mod tests { fn knowledge_validators_accept_valid_models() { let article = RadrootsWikiArticle { d_tag: "soil-health".to_string(), - title: "Soil health".to_string(), + title: Some("Soil health".to_string()), content_djot: "# Soil health".to_string(), summary: None, topics: Vec::new(), @@ -1260,6 +1262,27 @@ mod tests { } #[test] + fn wiki_article_title_is_optional_but_not_blank() { + let mut article = RadrootsWikiArticle { + d_tag: "soil-health".to_string(), + title: None, + content_djot: "# Soil health".to_string(), + summary: None, + topics: Vec::new(), + references: Vec::new(), + forked_from: Vec::new(), + deferred_to: None, + }; + assert_eq!(validate_wiki_article(&article), Ok(())); + + article.title = Some(" ".to_string()); + assert_validation_error( + validate_wiki_article(&article), + RadrootsKnowledgeValidationError::EmptyField("title"), + ); + } + + #[test] fn knowledge_claims_require_citations_except_exact_uncited_types() { let mut claim = knowledge_claim(); claim.citation_spans.clear(); @@ -1293,7 +1316,7 @@ mod tests { fn knowledge_validators_reject_representative_invalid_models() { let mut article = RadrootsWikiArticle { d_tag: "soil-health".to_string(), - title: "Soil health".to_string(), + title: Some("Soil health".to_string()), content_djot: " ".to_string(), summary: None, topics: Vec::new(), diff --git a/crates/events_codec/src/knowledge/decode.rs b/crates/events_codec/src/knowledge/decode.rs @@ -317,7 +317,7 @@ pub fn wiki_article_from_event( ensure_kind(event.kind, KIND_WIKI_ARTICLE, "wiki article")?; let d_tag = required_one_value(&event.tags, TAG_D)?; validate_wiki_d_tag(&d_tag).map_err(|_| EventParseError::InvalidTag(TAG_D))?; - let title = required_one_value(&event.tags, TAG_TITLE)?; + let title = optional_one_value(&event.tags, TAG_TITLE)?; let summary = optional_one_value(&event.tags, TAG_SUMMARY)?; let topics = values(&event.tags, TAG_T); let references = event_refs(&event.tags, TAG_SOURCE)?; diff --git a/crates/events_codec/src/knowledge/encode.rs b/crates/events_codec/src/knowledge/encode.rs @@ -160,7 +160,7 @@ pub fn wiki_article_build_tags( validate_wiki_article(article).map_err(encode_validation_error)?; let mut tags = Vec::new(); tags.push(vec![TAG_D.to_string(), article.d_tag.clone()]); - push_value(&mut tags, TAG_TITLE, &article.title); + push_optional_value(&mut tags, TAG_TITLE, article.title.as_deref()); push_optional_value(&mut tags, TAG_SUMMARY, article.summary.as_deref()); push_topics(&mut tags, &article.topics); push_event_refs(&mut tags, TAG_SOURCE, &article.references); diff --git a/crates/events_codec/tests/knowledge.rs b/crates/events_codec/tests/knowledge.rs @@ -147,7 +147,7 @@ fn sign_parts(parts: WireEventParts) -> RadrootsNostrEvent { fn wiki_article() -> RadrootsWikiArticle { RadrootsWikiArticle { d_tag: "soil-health".to_string(), - title: "Soil health".to_string(), + title: Some("Soil health".to_string()), content_djot: "# Soil health".to_string(), summary: Some("Living soil basics".to_string()), topics: vec!["soil".to_string(), "health".to_string()], @@ -318,7 +318,7 @@ fn knowledge_codecs_roundtrip_all_contracts() { .data .data .title, - "Soil health" + Some("Soil health".to_string()) ); let redirect = RadrootsWikiRedirect { @@ -658,6 +658,21 @@ fn malformed_nip54_wiki_shapes_are_rejected() { } #[test] +fn wiki_article_codec_accepts_missing_title_tag() { + let mut article = wiki_article(); + article.title = None; + let article_event = event_from_parts(wiki_article_to_wire_parts(&article).unwrap()); + assert!( + !article_event + .tags + .iter() + .any(|tag| tag.first().map(String::as_str) == Some("title")) + ); + let decoded = wiki_article_from_event(article_event).unwrap(); + assert_eq!(decoded.data.data.title, None); +} + +#[test] fn semantic_validation_rejects_invalid_encode_models() { let mut article = wiki_article(); article.content_djot = String::new(); diff --git a/crates/test_fixtures/src/knowledge.rs b/crates/test_fixtures/src/knowledge.rs @@ -185,7 +185,7 @@ pub fn wiki_article_deferred_version_ref() -> RadrootsWikiArticleVersionRef { pub fn wiki_article() -> RadrootsWikiArticle { RadrootsWikiArticle { d_tag: "soil-health".to_string(), - title: "Soil health".to_string(), + title: Some("Soil health".to_string()), content_djot: "# Soil health\n\nLiving soil supports resilient local food systems." .to_string(), summary: Some("Living soil basics".to_string()),