lib

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

knowledge.rs (19487B)


      1 use radroots_event::envelope::kind::{
      2     KIND_CONTRIBUTION_ATTESTATION, KIND_EVIDENCE_BOUNTY, KIND_FILE_METADATA,
      3     KIND_KNOWLEDGE_CHANGE_PROPOSAL, KIND_KNOWLEDGE_CLAIM, KIND_KNOWLEDGE_FIELD_REPORT,
      4     KIND_KNOWLEDGE_RELATION, KIND_KNOWLEDGE_REVIEW, KIND_KNOWLEDGE_SOURCE, KIND_WIKI_ARTICLE,
      5     KIND_WIKI_MERGE_REQUEST, KIND_WIKI_REDIRECT,
      6 };
      7 use radroots_event::knowledge::{
      8     AddressableRef, ContributionAttestation, EvidenceBounty, KnowledgeChangeProposal,
      9     KnowledgeCitationSpan, KnowledgeClaim, KnowledgeFieldContext, KnowledgeFieldReport,
     10     KnowledgeLocation, KnowledgeLocationPrecision, KnowledgeNodeRef, KnowledgeObservation,
     11     KnowledgeObservationValue, KnowledgeRelation, KnowledgeReview, KnowledgeReviewScope,
     12     KnowledgeReviewScore, KnowledgeReviewTarget, KnowledgeSource,
     13     RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA, RADROOTS_EVIDENCE_BOUNTY_SCHEMA,
     14     RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA, RADROOTS_KNOWLEDGE_CLAIM_SCHEMA,
     15     RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA, RADROOTS_KNOWLEDGE_RELATION_SCHEMA,
     16     RADROOTS_KNOWLEDGE_REVIEW_SCHEMA, RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
     17     RADROOTS_KNOWLEDGE_SOURCE_SCHEMA, WikiArticle, WikiArticleVersionRef, WikiMergeRequest,
     18     WikiRedirect,
     19 };
     20 use radroots_event::tag::EventRef;
     21 use radroots_identity::PublicKey;
     22 
     23 use super::{FIXTURE_ALICE_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS};
     24 
     25 pub const RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE: &str = "radroots-knowledge-fixture-v1";
     26 
     27 pub const RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS: [&str; 11] = [
     28     "radroots.wiki.article.v1",
     29     "radroots.wiki.redirect.v1",
     30     "radroots.wiki.merge_request.v1",
     31     RADROOTS_KNOWLEDGE_SOURCE_SCHEMA,
     32     RADROOTS_KNOWLEDGE_CLAIM_SCHEMA,
     33     RADROOTS_KNOWLEDGE_RELATION_SCHEMA,
     34     RADROOTS_KNOWLEDGE_REVIEW_SCHEMA,
     35     RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA,
     36     RADROOTS_EVIDENCE_BOUNTY_SCHEMA,
     37     RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA,
     38     RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA,
     39 ];
     40 
     41 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     42 pub struct RadrootsKnowledgeAdversarialFixture {
     43     pub id: &'static str,
     44     pub pipeline_stage: &'static str,
     45     pub expected_error_code: &'static str,
     46 }
     47 
     48 pub const RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES: [RadrootsKnowledgeAdversarialFixture; 13] = [
     49     RadrootsKnowledgeAdversarialFixture {
     50         id: "malformed_tags",
     51         pipeline_stage: "contract_validation",
     52         expected_error_code: "tag_cardinality_mismatch",
     53     },
     54     RadrootsKnowledgeAdversarialFixture {
     55         id: "wrong_schema",
     56         pipeline_stage: "contract_validation",
     57         expected_error_code: "content_field_mismatch",
     58     },
     59     RadrootsKnowledgeAdversarialFixture {
     60         id: "missing_contract_id",
     61         pipeline_stage: "contract_validation",
     62         expected_error_code: "contract_match",
     63     },
     64     RadrootsKnowledgeAdversarialFixture {
     65         id: "private_coordinate_leakage",
     66         pipeline_stage: "event_parse",
     67         expected_error_code: "invalid_json",
     68     },
     69     RadrootsKnowledgeAdversarialFixture {
     70         id: "unsupported_contract_shape",
     71         pipeline_stage: "contract_validation",
     72         expected_error_code: "contract_match",
     73     },
     74     RadrootsKnowledgeAdversarialFixture {
     75         id: "invalid_nip54_d_tag",
     76         pipeline_stage: "encode",
     77         expected_error_code: "invalid_field",
     78     },
     79     RadrootsKnowledgeAdversarialFixture {
     80         id: "invalid_redirect_target_kind",
     81         pipeline_stage: "encode",
     82         expected_error_code: "invalid_field",
     83     },
     84     RadrootsKnowledgeAdversarialFixture {
     85         id: "merge_request_missing_source_marker",
     86         pipeline_stage: "event_parse",
     87         expected_error_code: "invalid_tag",
     88     },
     89     RadrootsKnowledgeAdversarialFixture {
     90         id: "merge_request_json_content_guard",
     91         pipeline_stage: "wire_shape",
     92         expected_error_code: "plain_text_content",
     93     },
     94     RadrootsKnowledgeAdversarialFixture {
     95         id: "orphan_fork_marker",
     96         pipeline_stage: "event_parse",
     97         expected_error_code: "invalid_tag",
     98     },
     99     RadrootsKnowledgeAdversarialFixture {
    100         id: "orphan_defer_marker",
    101         pipeline_stage: "event_parse",
    102         expected_error_code: "invalid_tag",
    103     },
    104     RadrootsKnowledgeAdversarialFixture {
    105         id: "id_mismatch",
    106         pipeline_stage: "nip01_verification",
    107         expected_error_code: "id_mismatch",
    108     },
    109     RadrootsKnowledgeAdversarialFixture {
    110         id: "signature_invalidity",
    111         pipeline_stage: "nip01_verification",
    112         expected_error_code: "signature_invalid",
    113     },
    114 ];
    115 
    116 #[derive(Clone, Debug, PartialEq, Eq)]
    117 pub enum RadrootsKnowledgeFixture {
    118     WikiArticle(WikiArticle),
    119     WikiRedirect(WikiRedirect),
    120     WikiMergeRequest(WikiMergeRequest),
    121     KnowledgeSource(KnowledgeSource),
    122     KnowledgeClaim(KnowledgeClaim),
    123     KnowledgeRelation(KnowledgeRelation),
    124     KnowledgeReview(KnowledgeReview),
    125     KnowledgeFieldReport(KnowledgeFieldReport),
    126     EvidenceBounty(EvidenceBounty),
    127     KnowledgeChangeProposal(KnowledgeChangeProposal),
    128     ContributionAttestation(ContributionAttestation),
    129 }
    130 
    131 #[derive(Clone, Debug, PartialEq, Eq)]
    132 pub struct RadrootsKnowledgeFixtureCase {
    133     pub id: &'static str,
    134     pub contract_id: &'static str,
    135     pub kind: u32,
    136     pub data: RadrootsKnowledgeFixture,
    137 }
    138 
    139 pub fn hex_64(character: char) -> String {
    140     core::iter::repeat_n(character, 64).collect()
    141 }
    142 
    143 pub fn event_ref(character: char, kind: u32) -> EventRef {
    144     EventRef {
    145         id: hex_64(character),
    146         author: PublicKey::from_hex(FIXTURE_ALICE_PUBLIC_KEY_HEX)
    147             .expect("fixture public key is valid"),
    148         kind,
    149         d_tag: None,
    150         relays: Some(vec![RELAY_PRIMARY_WSS.to_string()]),
    151     }
    152 }
    153 
    154 pub fn address_ref() -> AddressableRef {
    155     AddressableRef {
    156         kind: KIND_WIKI_ARTICLE,
    157         pubkey: hex_64('a'),
    158         d_tag: "soil-health".to_string(),
    159         relays: vec![RELAY_PRIMARY_WSS.to_string()],
    160     }
    161 }
    162 
    163 pub fn deferred_address_ref() -> AddressableRef {
    164     AddressableRef {
    165         kind: KIND_WIKI_ARTICLE,
    166         pubkey: hex_64('a'),
    167         d_tag: "soil-health-v2".to_string(),
    168         relays: vec![RELAY_PRIMARY_WSS.to_string()],
    169     }
    170 }
    171 
    172 pub fn wiki_article_version_ref() -> WikiArticleVersionRef {
    173     WikiArticleVersionRef {
    174         event_id: hex_64('b'),
    175         address_ref: address_ref(),
    176     }
    177 }
    178 
    179 pub fn wiki_article_deferred_version_ref() -> WikiArticleVersionRef {
    180     WikiArticleVersionRef {
    181         event_id: hex_64('c'),
    182         address_ref: deferred_address_ref(),
    183     }
    184 }
    185 
    186 pub fn wiki_article() -> WikiArticle {
    187     WikiArticle {
    188         d_tag: "soil-health".to_string(),
    189         title: Some("Soil health".to_string()),
    190         content_djot: "# Soil health\n\nLiving soil supports resilient local food systems."
    191             .to_string(),
    192         summary: Some("Living soil basics".to_string()),
    193         topics: vec!["soil".to_string(), "local-food".to_string()],
    194         references: vec![event_ref('2', KIND_KNOWLEDGE_SOURCE)],
    195         forked_from: vec![wiki_article_version_ref()],
    196         deferred_to: Some(wiki_article_deferred_version_ref()),
    197     }
    198 }
    199 
    200 pub fn wiki_redirect() -> WikiRedirect {
    201     WikiRedirect {
    202         d_tag: "soil".to_string(),
    203         target: address_ref(),
    204     }
    205 }
    206 
    207 pub fn wiki_merge_request() -> WikiMergeRequest {
    208     WikiMergeRequest {
    209         target_article: address_ref(),
    210         destination_pubkey: hex_64('a'),
    211         base_version_event_id: Some(hex_64('e')),
    212         source_version_event_id: hex_64('f'),
    213         explanation: Some("Merge synthetic soil article updates".to_string()),
    214     }
    215 }
    216 
    217 pub fn wiki_merge_request_without_base_version() -> WikiMergeRequest {
    218     WikiMergeRequest {
    219         base_version_event_id: None,
    220         ..wiki_merge_request()
    221     }
    222 }
    223 
    224 pub fn knowledge_source() -> KnowledgeSource {
    225     KnowledgeSource {
    226         schema: RADROOTS_KNOWLEDGE_SOURCE_SCHEMA.to_string(),
    227         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    228         d_tag: "soil-source".to_string(),
    229         title: "Soil Source".to_string(),
    230         source_type: "book".to_string(),
    231         authors: vec!["A. Example".to_string()],
    232         publisher: Some("Radroots Synthetic Press".to_string()),
    233         publication_year: Some(2026),
    234         edition: None,
    235         canonical_url: Some("https://source.example.test/soil-source".to_string()),
    236         artifact_refs: vec![event_ref('3', KIND_FILE_METADATA)],
    237         author_asserted_rights: None,
    238         topics: vec!["soil".to_string()],
    239         summary: Some("Synthetic source for knowledge fixture coverage".to_string()),
    240     }
    241 }
    242 
    243 pub fn knowledge_claim() -> KnowledgeClaim {
    244     KnowledgeClaim {
    245         schema: RADROOTS_KNOWLEDGE_CLAIM_SCHEMA.to_string(),
    246         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    247         claim_type: "practice_effect".to_string(),
    248         text: "Cover crops improve soil structure.".to_string(),
    249         citation_spans: vec![KnowledgeCitationSpan {
    250             source_ref: event_ref('4', KIND_KNOWLEDGE_SOURCE),
    251             artifact_ref: None,
    252             page_start: Some(12),
    253             page_end: Some(13),
    254             section_path: vec!["chapter-1".to_string()],
    255             quote_hash: Some(hex_64('5')),
    256             chunk_id: Some("chunk-1".to_string()),
    257         }],
    258         topics: vec!["cover-crops".to_string()],
    259         applies_to: vec!["local-food".to_string()],
    260         author_asserted_confidence: Some("medium".to_string()),
    261         supersedes: Vec::new(),
    262     }
    263 }
    264 
    265 pub fn knowledge_node_ref(label: &str) -> KnowledgeNodeRef {
    266     KnowledgeNodeRef {
    267         node_type: "event".to_string(),
    268         event_ref: Some(event_ref('6', KIND_KNOWLEDGE_CLAIM)),
    269         address_ref: None,
    270         external_id: None,
    271         label: Some(label.to_string()),
    272     }
    273 }
    274 
    275 pub fn knowledge_relation() -> KnowledgeRelation {
    276     KnowledgeRelation {
    277         schema: RADROOTS_KNOWLEDGE_RELATION_SCHEMA.to_string(),
    278         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    279         subject: knowledge_node_ref("cover crops"),
    280         predicate: "supports".to_string(),
    281         object: knowledge_node_ref("soil structure"),
    282         support_refs: vec![event_ref('7', KIND_KNOWLEDGE_CLAIM)],
    283         author_asserted_confidence: Some("medium".to_string()),
    284         supersedes: Vec::new(),
    285     }
    286 }
    287 
    288 pub fn knowledge_review() -> KnowledgeReview {
    289     KnowledgeReview {
    290         schema: RADROOTS_KNOWLEDGE_REVIEW_SCHEMA.to_string(),
    291         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    292         target: KnowledgeReviewTarget {
    293             event_id: hex_64('8'),
    294             author_pubkey: hex_64('a'),
    295             kind: KIND_KNOWLEDGE_CLAIM,
    296             address: None,
    297             relays: vec![RELAY_PRIMARY_WSS.to_string()],
    298             review_scope: KnowledgeReviewScope::SpecificVersion,
    299         },
    300         reviewer_role: "peer".to_string(),
    301         verdict: "needs_more_evidence".to_string(),
    302         scores: vec![KnowledgeReviewScore {
    303             dimension: "evidence".to_string(),
    304             value: "partial".to_string(),
    305             note: None,
    306         }],
    307         notes: Some("Synthetic review".to_string()),
    308         evidence_refs: vec![event_ref('9', KIND_KNOWLEDGE_SOURCE)],
    309     }
    310 }
    311 
    312 pub fn knowledge_field_report() -> KnowledgeFieldReport {
    313     KnowledgeFieldReport {
    314         schema: RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA.to_string(),
    315         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    316         report_type: "observation".to_string(),
    317         title: "Field observation".to_string(),
    318         summary: Some("Observed cover crop residue.".to_string()),
    319         context: KnowledgeFieldContext {
    320             location_precision: KnowledgeLocationPrecision::CoarseGeohash,
    321             public_location: Some(KnowledgeLocation {
    322                 label: Some("watershed".to_string()),
    323                 region: Some("synthetic-region".to_string()),
    324                 locality: None,
    325                 geohash: Some("c23".to_string()),
    326             }),
    327             private_location_ref: None,
    328             topics: vec!["field".to_string()],
    329             context_tags: vec!["observation".to_string()],
    330         },
    331         observations: vec![KnowledgeObservation {
    332             observation_type: "residue".to_string(),
    333             text: "Residue was visible across beds.".to_string(),
    334             observed_at: Some("2026-07-05".to_string()),
    335             values: vec![KnowledgeObservationValue {
    336                 key: "coverage".to_string(),
    337                 value: "medium".to_string(),
    338                 unit: None,
    339             }],
    340         }],
    341         artifact_refs: vec![event_ref('c', KIND_FILE_METADATA)],
    342         related_refs: vec![event_ref('d', KIND_KNOWLEDGE_CLAIM)],
    343         limitations: vec!["single observer".to_string()],
    344     }
    345 }
    346 
    347 pub fn evidence_bounty() -> EvidenceBounty {
    348     EvidenceBounty {
    349         schema: RADROOTS_EVIDENCE_BOUNTY_SCHEMA.to_string(),
    350         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    351         d_tag: "soil-bounty".to_string(),
    352         title: "Soil bounty".to_string(),
    353         summary: None,
    354         topics: vec!["soil".to_string()],
    355         target_refs: vec![event_ref('a', KIND_KNOWLEDGE_CLAIM)],
    356         reward_note: None,
    357         closes_at: None,
    358     }
    359 }
    360 
    361 pub fn knowledge_change_proposal() -> KnowledgeChangeProposal {
    362     KnowledgeChangeProposal {
    363         schema: RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA.to_string(),
    364         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    365         target: event_ref('b', KIND_KNOWLEDGE_CLAIM),
    366         proposal_type: "amend".to_string(),
    367         summary: "Clarify scope".to_string(),
    368         rationale: None,
    369         evidence_refs: vec![event_ref('c', KIND_KNOWLEDGE_SOURCE)],
    370         supersedes: Vec::new(),
    371     }
    372 }
    373 
    374 pub fn contribution_attestation() -> ContributionAttestation {
    375     ContributionAttestation {
    376         schema: RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA.to_string(),
    377         schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION,
    378         contributor_pubkey: hex_64('a'),
    379         contribution_type: "review".to_string(),
    380         subject_refs: vec![event_ref('d', KIND_KNOWLEDGE_REVIEW)],
    381         summary: "Reviewed synthetic claim".to_string(),
    382         evidence_refs: vec![event_ref('e', KIND_KNOWLEDGE_REVIEW)],
    383     }
    384 }
    385 
    386 pub fn knowledge_valid_fixtures() -> Vec<RadrootsKnowledgeFixtureCase> {
    387     vec![
    388         RadrootsKnowledgeFixtureCase {
    389             id: "wiki_article_valid",
    390             contract_id: "radroots.wiki.article.v1",
    391             kind: KIND_WIKI_ARTICLE,
    392             data: RadrootsKnowledgeFixture::WikiArticle(wiki_article()),
    393         },
    394         RadrootsKnowledgeFixtureCase {
    395             id: "wiki_redirect_valid",
    396             contract_id: "radroots.wiki.redirect.v1",
    397             kind: KIND_WIKI_REDIRECT,
    398             data: RadrootsKnowledgeFixture::WikiRedirect(wiki_redirect()),
    399         },
    400         RadrootsKnowledgeFixtureCase {
    401             id: "wiki_merge_request_valid",
    402             contract_id: "radroots.wiki.merge_request.v1",
    403             kind: KIND_WIKI_MERGE_REQUEST,
    404             data: RadrootsKnowledgeFixture::WikiMergeRequest(wiki_merge_request()),
    405         },
    406         RadrootsKnowledgeFixtureCase {
    407             id: "wiki_merge_request_without_base_valid",
    408             contract_id: "radroots.wiki.merge_request.v1",
    409             kind: KIND_WIKI_MERGE_REQUEST,
    410             data: RadrootsKnowledgeFixture::WikiMergeRequest(
    411                 wiki_merge_request_without_base_version(),
    412             ),
    413         },
    414         RadrootsKnowledgeFixtureCase {
    415             id: "knowledge_source_valid",
    416             contract_id: RADROOTS_KNOWLEDGE_SOURCE_SCHEMA,
    417             kind: KIND_KNOWLEDGE_SOURCE,
    418             data: RadrootsKnowledgeFixture::KnowledgeSource(knowledge_source()),
    419         },
    420         RadrootsKnowledgeFixtureCase {
    421             id: "knowledge_claim_valid",
    422             contract_id: RADROOTS_KNOWLEDGE_CLAIM_SCHEMA,
    423             kind: KIND_KNOWLEDGE_CLAIM,
    424             data: RadrootsKnowledgeFixture::KnowledgeClaim(knowledge_claim()),
    425         },
    426         RadrootsKnowledgeFixtureCase {
    427             id: "knowledge_relation_valid",
    428             contract_id: RADROOTS_KNOWLEDGE_RELATION_SCHEMA,
    429             kind: KIND_KNOWLEDGE_RELATION,
    430             data: RadrootsKnowledgeFixture::KnowledgeRelation(knowledge_relation()),
    431         },
    432         RadrootsKnowledgeFixtureCase {
    433             id: "knowledge_review_valid",
    434             contract_id: RADROOTS_KNOWLEDGE_REVIEW_SCHEMA,
    435             kind: KIND_KNOWLEDGE_REVIEW,
    436             data: RadrootsKnowledgeFixture::KnowledgeReview(knowledge_review()),
    437         },
    438         RadrootsKnowledgeFixtureCase {
    439             id: "knowledge_field_report_valid",
    440             contract_id: RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA,
    441             kind: KIND_KNOWLEDGE_FIELD_REPORT,
    442             data: RadrootsKnowledgeFixture::KnowledgeFieldReport(knowledge_field_report()),
    443         },
    444         RadrootsKnowledgeFixtureCase {
    445             id: "evidence_bounty_valid",
    446             contract_id: RADROOTS_EVIDENCE_BOUNTY_SCHEMA,
    447             kind: KIND_EVIDENCE_BOUNTY,
    448             data: RadrootsKnowledgeFixture::EvidenceBounty(evidence_bounty()),
    449         },
    450         RadrootsKnowledgeFixtureCase {
    451             id: "knowledge_change_proposal_valid",
    452             contract_id: RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA,
    453             kind: KIND_KNOWLEDGE_CHANGE_PROPOSAL,
    454             data: RadrootsKnowledgeFixture::KnowledgeChangeProposal(knowledge_change_proposal()),
    455         },
    456         RadrootsKnowledgeFixtureCase {
    457             id: "contribution_attestation_valid",
    458             contract_id: RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA,
    459             kind: KIND_CONTRIBUTION_ATTESTATION,
    460             data: RadrootsKnowledgeFixture::ContributionAttestation(contribution_attestation()),
    461         },
    462     ]
    463 }
    464 
    465 #[cfg(test)]
    466 #[cfg_attr(coverage_nightly, coverage(off))]
    467 mod tests {
    468     use super::{
    469         RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES, RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE,
    470         RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS, knowledge_valid_fixtures,
    471     };
    472 
    473     #[test]
    474     fn valid_fixture_catalog_covers_all_contract_ids() {
    475         let fixtures = knowledge_valid_fixtures();
    476         assert!(fixtures.len() >= RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS.len());
    477         for contract_id in RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS {
    478             assert!(
    479                 fixtures
    480                     .iter()
    481                     .any(|fixture| fixture.contract_id == contract_id),
    482                 "{contract_id}"
    483             );
    484         }
    485         assert_eq!(
    486             RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE,
    487             "radroots-knowledge-fixture-v1"
    488         );
    489     }
    490 
    491     #[test]
    492     fn adversarial_fixture_catalog_covers_required_cases() {
    493         let expected = [
    494             "malformed_tags",
    495             "wrong_schema",
    496             "missing_contract_id",
    497             "private_coordinate_leakage",
    498             "unsupported_contract_shape",
    499             "invalid_nip54_d_tag",
    500             "invalid_redirect_target_kind",
    501             "merge_request_missing_source_marker",
    502             "merge_request_json_content_guard",
    503             "orphan_fork_marker",
    504             "orphan_defer_marker",
    505             "id_mismatch",
    506             "signature_invalidity",
    507         ];
    508         assert_eq!(
    509             RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES.len(),
    510             expected.len()
    511         );
    512         for id in expected {
    513             assert!(
    514                 RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES
    515                     .iter()
    516                     .any(|fixture| fixture.id == id),
    517                 "{id}"
    518             );
    519         }
    520     }
    521 }