commit 79a1e89995f8c397002f64d1d706714019211d1a
parent eb9246e86c9a72afde35ecb2fd994572250fcf07
Author: triesap <tyson@radroots.org>
Date: Sun, 5 Jul 2026 20:00:50 +0000
events-codec: harden knowledge manifest contracts
Diffstat:
9 files changed, 996 insertions(+), 664 deletions(-)
diff --git a/contracts/conformance/vectors/knowledge/manifest_and_decode.v1.json b/contracts/conformance/vectors/knowledge/manifest_and_decode.v1.json
@@ -9,7 +9,7 @@
"registry": "radroots_event_contract_registry_v1"
},
"expected": {
- "schema_version": 1,
+ "schema_version": 2,
"required_fields": [
"schema_version",
"registry_version",
@@ -26,6 +26,9 @@
"tag_contracts",
"codec_support",
"sdk_builder_support",
+ "sdk_draft_support",
+ "wasm_tag_builder_support",
+ "wasm_verified_decode_support",
"deprecated",
"replaced_by",
"introduced_at"
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v1.json b/contracts/knowledge/knowledge_event_contract_manifest.v1.json
@@ -1,636 +0,0 @@
-{
- "schema_version": 1,
- "registry_version": 1,
- "radroots_events_version": "0.1.0-alpha.2",
- "radroots_events_codec_version": "0.1.0-alpha.2",
- "contract_count": 11,
- "contracts": [
- {
- "contract_id": "radroots.knowledge.change_proposal.v1",
- "kind": 3464,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeChangeProposal",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.change_proposal.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "evidence",
- "cardinality": "optional_many",
- "semantic": "evidence",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.claim.v1",
- "kind": 3460,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeClaim",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.claim.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- },
- {
- "name": "source",
- "cardinality": "optional_many",
- "semantic": "source",
- "value_type": "event_pointer",
- "relay_indexed": false
- },
- {
- "name": "citation",
- "cardinality": "optional_many",
- "semantic": "citation",
- "value_type": "sha256",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.contribution_attestation.v1",
- "kind": 3465,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsContributionAttestation",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.contribution_attestation.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "evidence",
- "cardinality": "optional_many",
- "semantic": "evidence",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.evidence_bounty.v1",
- "kind": 30451,
- "class": "addressable",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsEvidenceBounty",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.evidence_bounty.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "d",
- "cardinality": "required_one",
- "semantic": "identifier",
- "value_type": "d_tag",
- "relay_indexed": true
- },
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- },
- {
- "name": "evidence",
- "cardinality": "optional_many",
- "semantic": "evidence",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.field_report.v1",
- "kind": 3463,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeFieldReport",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.field_report.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- },
- {
- "name": "g",
- "cardinality": "optional_one",
- "semantic": "geohash",
- "value_type": "geohash",
- "relay_indexed": true
- },
- {
- "name": "evidence",
- "cardinality": "optional_many",
- "semantic": "evidence",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.relation.v1",
- "kind": 3461,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeRelation",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.relation.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- },
- {
- "name": "source",
- "cardinality": "optional_many",
- "semantic": "source",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.review.v1",
- "kind": 3462,
- "class": "regular",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeReview",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.review.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "review_target",
- "cardinality": "required_one",
- "semantic": "review_target",
- "value_type": "event_pointer",
- "relay_indexed": false
- },
- {
- "name": "evidence",
- "cardinality": "optional_many",
- "semantic": "evidence",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.knowledge.source.v1",
- "kind": 30450,
- "class": "addressable",
- "standard": "radroots",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsKnowledgeSource",
- "discriminators": [
- {
- "type": "tag_equals",
- "name": "contract",
- "value": "radroots.knowledge.source.v1"
- }
- ],
- "tag_contracts": [
- {
- "name": "d",
- "cardinality": "required_one",
- "semantic": "identifier",
- "value_type": "d_tag",
- "relay_indexed": true
- },
- {
- "name": "contract",
- "cardinality": "required_one",
- "semantic": "contract",
- "value_type": "contract_id",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- },
- {
- "name": "source",
- "cardinality": "optional_many",
- "semantic": "source",
- "value_type": "event_pointer",
- "relay_indexed": false
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.wiki.article.v1",
- "kind": 30818,
- "class": "addressable",
- "standard": "nip54",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "djot",
- "payload_type": "RadrootsWikiArticle",
- "discriminators": [
- {
- "type": "kind_only"
- }
- ],
- "tag_contracts": [
- {
- "name": "d",
- "cardinality": "required_one",
- "semantic": "identifier",
- "value_type": "d_tag",
- "relay_indexed": true
- },
- {
- "name": "title",
- "cardinality": "optional_one",
- "semantic": "title",
- "value_type": "text",
- "relay_indexed": false
- },
- {
- "name": "summary",
- "cardinality": "optional_one",
- "semantic": "summary",
- "value_type": "text",
- "relay_indexed": false
- },
- {
- "name": "published_at",
- "cardinality": "optional_one",
- "semantic": "published_at",
- "value_type": "unix_timestamp",
- "relay_indexed": false
- },
- {
- "name": "t",
- "cardinality": "optional_many",
- "semantic": "topic",
- "value_type": "text",
- "relay_indexed": true
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.wiki.merge_request.v1",
- "kind": 818,
- "class": "regular",
- "standard": "nip54",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "json_object",
- "payload_type": "RadrootsWikiMergeRequest",
- "discriminators": [
- {
- "type": "kind_only"
- }
- ],
- "tag_contracts": [
- {
- "name": "a",
- "cardinality": "required_one",
- "semantic": "addressable_coordinate",
- "value_type": "addressable_coordinate",
- "relay_indexed": true
- },
- {
- "name": "p",
- "cardinality": "required_one",
- "semantic": "counterparty",
- "value_type": "public_key",
- "relay_indexed": true
- },
- {
- "name": "e",
- "cardinality": "required_one",
- "semantic": "source",
- "value_type": "event_id",
- "relay_indexed": true
- },
- {
- "name": "e",
- "cardinality": "optional_one",
- "semantic": "previous_event",
- "value_type": "event_id",
- "relay_indexed": true
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- },
- {
- "contract_id": "radroots.wiki.redirect.v1",
- "kind": 30819,
- "class": "addressable",
- "standard": "nip54",
- "stability": "stable",
- "privacy": "public",
- "author_role": "any",
- "content_schema": "empty",
- "payload_type": "RadrootsWikiRedirect",
- "discriminators": [
- {
- "type": "kind_only"
- }
- ],
- "tag_contracts": [
- {
- "name": "d",
- "cardinality": "required_one",
- "semantic": "identifier",
- "value_type": "d_tag",
- "relay_indexed": true
- },
- {
- "name": "a",
- "cardinality": "required_one",
- "semantic": "addressable_coordinate",
- "value_type": "addressable_coordinate",
- "relay_indexed": true
- }
- ],
- "reducers": [
- "knowledge_projection"
- ],
- "codec_support": {
- "encode": true,
- "decode": true,
- "contract_validation": true,
- "verified_decode": true,
- "verified_decode_requires_nostr": true
- },
- "sdk_builder_support": false,
- "deprecated": false,
- "replaced_by": null,
- "introduced_at": "0.1.0-alpha.2"
- }
- ]
-}
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v1.sha256 b/contracts/knowledge/knowledge_event_contract_manifest.v1.sha256
@@ -1 +0,0 @@
-8e685748e731767c6a81ce0721a674567dd8010e800d21cd5e12688da1b73757
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v2.json b/contracts/knowledge/knowledge_event_contract_manifest.v2.json
@@ -0,0 +1,669 @@
+{
+ "schema_version": 2,
+ "registry_version": 1,
+ "radroots_events_version": "0.1.0-alpha.2",
+ "radroots_events_codec_version": "0.1.0-alpha.2",
+ "contract_count": 11,
+ "contracts": [
+ {
+ "contract_id": "radroots.knowledge.change_proposal.v1",
+ "kind": 3464,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeChangeProposal",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.change_proposal.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "evidence",
+ "cardinality": "optional_many",
+ "semantic": "evidence",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": false,
+ "sdk_draft_support": false,
+ "wasm_tag_builder_support": false,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.claim.v1",
+ "kind": 3460,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeClaim",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.claim.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ },
+ {
+ "name": "source",
+ "cardinality": "optional_many",
+ "semantic": "source",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ },
+ {
+ "name": "citation",
+ "cardinality": "optional_many",
+ "semantic": "citation",
+ "value_type": "sha256",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.contribution_attestation.v1",
+ "kind": 3465,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsContributionAttestation",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.contribution_attestation.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "evidence",
+ "cardinality": "optional_many",
+ "semantic": "evidence",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": false,
+ "sdk_draft_support": false,
+ "wasm_tag_builder_support": false,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.evidence_bounty.v1",
+ "kind": 30451,
+ "class": "addressable",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsEvidenceBounty",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.evidence_bounty.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "d",
+ "cardinality": "required_one",
+ "semantic": "identifier",
+ "value_type": "d_tag",
+ "relay_indexed": true
+ },
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ },
+ {
+ "name": "evidence",
+ "cardinality": "optional_many",
+ "semantic": "evidence",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": false,
+ "sdk_draft_support": false,
+ "wasm_tag_builder_support": false,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.field_report.v1",
+ "kind": 3463,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeFieldReport",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.field_report.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ },
+ {
+ "name": "g",
+ "cardinality": "optional_one",
+ "semantic": "geohash",
+ "value_type": "geohash",
+ "relay_indexed": true
+ },
+ {
+ "name": "evidence",
+ "cardinality": "optional_many",
+ "semantic": "evidence",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.relation.v1",
+ "kind": 3461,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeRelation",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.relation.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ },
+ {
+ "name": "source",
+ "cardinality": "optional_many",
+ "semantic": "source",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.review.v1",
+ "kind": 3462,
+ "class": "regular",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeReview",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.review.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "review_target",
+ "cardinality": "required_one",
+ "semantic": "review_target",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ },
+ {
+ "name": "evidence",
+ "cardinality": "optional_many",
+ "semantic": "evidence",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.knowledge.source.v1",
+ "kind": 30450,
+ "class": "addressable",
+ "standard": "radroots",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "json_object",
+ "payload_type": "RadrootsKnowledgeSource",
+ "discriminators": [
+ {
+ "type": "tag_equals",
+ "name": "contract",
+ "value": "radroots.knowledge.source.v1"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "d",
+ "cardinality": "required_one",
+ "semantic": "identifier",
+ "value_type": "d_tag",
+ "relay_indexed": true
+ },
+ {
+ "name": "contract",
+ "cardinality": "required_one",
+ "semantic": "contract",
+ "value_type": "contract_id",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ },
+ {
+ "name": "source",
+ "cardinality": "optional_many",
+ "semantic": "source",
+ "value_type": "event_pointer",
+ "relay_indexed": false
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.wiki.article.v1",
+ "kind": 30818,
+ "class": "addressable",
+ "standard": "nip54",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "djot",
+ "payload_type": "RadrootsWikiArticle",
+ "discriminators": [
+ {
+ "type": "kind_only"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "d",
+ "cardinality": "required_one",
+ "semantic": "identifier",
+ "value_type": "d_tag",
+ "relay_indexed": true
+ },
+ {
+ "name": "title",
+ "cardinality": "optional_one",
+ "semantic": "title",
+ "value_type": "text",
+ "relay_indexed": false
+ },
+ {
+ "name": "summary",
+ "cardinality": "optional_one",
+ "semantic": "summary",
+ "value_type": "text",
+ "relay_indexed": false
+ },
+ {
+ "name": "published_at",
+ "cardinality": "optional_one",
+ "semantic": "published_at",
+ "value_type": "unix_timestamp",
+ "relay_indexed": false
+ },
+ {
+ "name": "t",
+ "cardinality": "optional_many",
+ "semantic": "topic",
+ "value_type": "text",
+ "relay_indexed": true
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.wiki.merge_request.v1",
+ "kind": 818,
+ "class": "regular",
+ "standard": "nip54",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "plain_text",
+ "payload_type": "RadrootsWikiMergeRequest",
+ "discriminators": [
+ {
+ "type": "kind_only"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "a",
+ "cardinality": "required_one",
+ "semantic": "addressable_coordinate",
+ "value_type": "addressable_coordinate",
+ "relay_indexed": true
+ },
+ {
+ "name": "p",
+ "cardinality": "required_one",
+ "semantic": "counterparty",
+ "value_type": "public_key",
+ "relay_indexed": true
+ },
+ {
+ "name": "e",
+ "cardinality": "required_one",
+ "semantic": "source",
+ "value_type": "event_id",
+ "relay_indexed": true
+ },
+ {
+ "name": "e",
+ "cardinality": "optional_one",
+ "semantic": "previous_event",
+ "value_type": "event_id",
+ "relay_indexed": true
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ },
+ {
+ "contract_id": "radroots.wiki.redirect.v1",
+ "kind": 30819,
+ "class": "addressable",
+ "standard": "nip54",
+ "stability": "experimental",
+ "privacy": "public",
+ "author_role": "any",
+ "content_schema": "empty",
+ "payload_type": "RadrootsWikiRedirect",
+ "discriminators": [
+ {
+ "type": "kind_only"
+ }
+ ],
+ "tag_contracts": [
+ {
+ "name": "d",
+ "cardinality": "required_one",
+ "semantic": "identifier",
+ "value_type": "d_tag",
+ "relay_indexed": true
+ },
+ {
+ "name": "a",
+ "cardinality": "required_one",
+ "semantic": "addressable_coordinate",
+ "value_type": "addressable_coordinate",
+ "relay_indexed": true
+ }
+ ],
+ "reducers": [
+ "knowledge_projection"
+ ],
+ "codec_support": {
+ "encode": true,
+ "decode": true,
+ "contract_validation": true,
+ "verified_decode": true,
+ "verified_decode_requires_nostr": true
+ },
+ "sdk_builder_support": true,
+ "sdk_draft_support": true,
+ "wasm_tag_builder_support": true,
+ "wasm_verified_decode_support": true,
+ "deprecated": false,
+ "replaced_by": null,
+ "introduced_at": "0.1.0-alpha.2"
+ }
+ ]
+}
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v2.sha256 b/contracts/knowledge/knowledge_event_contract_manifest.v2.sha256
@@ -0,0 +1 @@
+8249f0d85f0f618ed5c7bd2088171df1c80bb2cd6aef9a1577cd78b46215953e
diff --git a/crates/events/src/contract.rs b/crates/events/src/contract.rs
@@ -778,7 +778,7 @@ macro_rules! kind_contract {
};
}
-macro_rules! event_contract {
+macro_rules! event_contract_with_stability {
(
$id:literal,
$kind:expr,
@@ -790,7 +790,8 @@ macro_rules! event_contract {
$content_schema:expr,
$discriminator:expr,
$tags:expr,
- $reducers:expr $(,)?
+ $reducers:expr,
+ $stability:expr $(,)?
) => {
RadrootsEventContract {
id: $id,
@@ -798,7 +799,7 @@ macro_rules! event_contract {
name: $name,
payload_type: $payload_type,
class: $class,
- stability: RadrootsEventStability::Stable,
+ stability: $stability,
privacy: $standard_privacy,
author_role: $author_role,
content_schema: $content_schema,
@@ -809,6 +810,68 @@ macro_rules! event_contract {
};
}
+macro_rules! event_contract {
+ (
+ $id:literal,
+ $kind:expr,
+ $name:literal,
+ $payload_type:literal,
+ $class:expr,
+ $standard_privacy:expr,
+ $author_role:expr,
+ $content_schema:expr,
+ $discriminator:expr,
+ $tags:expr,
+ $reducers:expr $(,)?
+ ) => {
+ event_contract_with_stability!(
+ $id,
+ $kind,
+ $name,
+ $payload_type,
+ $class,
+ $standard_privacy,
+ $author_role,
+ $content_schema,
+ $discriminator,
+ $tags,
+ $reducers,
+ RadrootsEventStability::Stable
+ )
+ };
+}
+
+macro_rules! experimental_event_contract {
+ (
+ $id:literal,
+ $kind:expr,
+ $name:literal,
+ $payload_type:literal,
+ $class:expr,
+ $standard_privacy:expr,
+ $author_role:expr,
+ $content_schema:expr,
+ $discriminator:expr,
+ $tags:expr,
+ $reducers:expr $(,)?
+ ) => {
+ event_contract_with_stability!(
+ $id,
+ $kind,
+ $name,
+ $payload_type,
+ $class,
+ $standard_privacy,
+ $author_role,
+ $content_schema,
+ $discriminator,
+ $tags,
+ $reducers,
+ RadrootsEventStability::Experimental
+ )
+ };
+}
+
static ALL_KIND_CONTRACTS: &[RadrootsKindContract] = &[
kind_contract!(
KIND_PROFILE,
@@ -2376,7 +2439,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
ARTICLE_TAGS,
SOCIAL_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.wiki.merge_request.v1",
KIND_WIKI_MERGE_REQUEST,
"Wiki Merge Request",
@@ -2389,7 +2452,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
WIKI_MERGE_REQUEST_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.wiki.article.v1",
KIND_WIKI_ARTICLE,
"Wiki Article",
@@ -2402,7 +2465,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
WIKI_ARTICLE_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.wiki.redirect.v1",
KIND_WIKI_REDIRECT,
"Wiki Redirect",
@@ -2623,7 +2686,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
LISTING_TAGS,
LISTING_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.source.v1",
KIND_KNOWLEDGE_SOURCE,
"Knowledge Source",
@@ -2639,7 +2702,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_SOURCE_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.evidence_bounty.v1",
KIND_EVIDENCE_BOUNTY,
"Evidence Bounty",
@@ -2655,7 +2718,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
EVIDENCE_BOUNTY_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.claim.v1",
KIND_KNOWLEDGE_CLAIM,
"Knowledge Claim",
@@ -2671,7 +2734,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_CLAIM_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.relation.v1",
KIND_KNOWLEDGE_RELATION,
"Knowledge Relation",
@@ -2687,7 +2750,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_RELATION_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.review.v1",
KIND_KNOWLEDGE_REVIEW,
"Knowledge Review",
@@ -2703,7 +2766,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_REVIEW_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.field_report.v1",
KIND_KNOWLEDGE_FIELD_REPORT,
"Knowledge Field Report",
@@ -2719,7 +2782,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_FIELD_REPORT_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.change_proposal.v1",
KIND_KNOWLEDGE_CHANGE_PROPOSAL,
"Knowledge Change Proposal",
@@ -2735,7 +2798,7 @@ static ALL_EVENT_CONTRACTS: &[RadrootsEventContract] = &[
KNOWLEDGE_CHANGE_PROPOSAL_TAGS,
KNOWLEDGE_REDUCERS
),
- event_contract!(
+ experimental_event_contract!(
"radroots.knowledge.contribution_attestation.v1",
KIND_CONTRIBUTION_ATTESTATION,
"Contribution Attestation",
@@ -3070,11 +3133,10 @@ pub fn event_contract(id: &str) -> Option<&'static RadrootsEventContract> {
.find(|contract| contract.id == id)
}
-pub fn event_contracts_for_kind(kind: u32) -> Vec<&'static RadrootsEventContract> {
+pub fn event_contracts_for_kind(kind: u32) -> impl Iterator<Item = &'static RadrootsEventContract> {
ALL_EVENT_CONTRACTS
.iter()
- .filter(|contract| contract.kind == kind)
- .collect()
+ .filter(move |contract| contract.kind == kind)
}
pub fn identify_event_contract(
@@ -3540,7 +3602,7 @@ mod tests {
#[test]
fn event_contract_lookup_supports_many_contracts_per_kind() {
- let contracts = event_contracts_for_kind(KIND_LIST_SET_GENERIC);
+ let contracts = event_contracts_for_kind(KIND_LIST_SET_GENERIC).collect::<Vec<_>>();
assert_eq!(contracts.len(), 6);
assert!(
contracts
@@ -3551,7 +3613,20 @@ mod tests {
event_contract("radroots.list_set.member_of.farms.v1").map(|contract| contract.kind),
Some(KIND_LIST_SET_GENERIC)
);
- assert!(event_contracts_for_kind(999_999).is_empty());
+ assert!(event_contracts_for_kind(999_999).next().is_none());
+ }
+
+ #[test]
+ fn event_contract_lookup_supports_knowledge_contract_kinds() {
+ let contracts = event_contracts_for_kind(KIND_WIKI_ARTICLE).collect::<Vec<_>>();
+ assert_eq!(contracts.len(), 1);
+ assert_eq!(contracts[0].id, "radroots.wiki.article.v1");
+ assert_eq!(
+ identify_event_contract(KIND_WIKI_ARTICLE, &[], "# Soil")
+ .expect("wiki article contract")
+ .id,
+ "radroots.wiki.article.v1"
+ );
}
#[test]
@@ -3580,6 +3655,7 @@ mod tests {
fn exposes_knowledge_contracts() {
let wiki_article = event_contract("radroots.wiki.article.v1").expect("wiki article");
assert_eq!(wiki_article.kind, KIND_WIKI_ARTICLE);
+ assert_eq!(wiki_article.stability, RadrootsEventStability::Experimental);
assert_eq!(
kind_contract(KIND_WIKI_ARTICLE)
.expect("wiki kind")
@@ -3588,8 +3664,23 @@ mod tests {
);
assert_eq!(wiki_article.content_schema, RadrootsContentSchema::Djot);
+ let wiki_merge_request =
+ event_contract("radroots.wiki.merge_request.v1").expect("wiki merge request");
+ assert_eq!(
+ wiki_merge_request.stability,
+ RadrootsEventStability::Experimental
+ );
+ assert_eq!(
+ wiki_merge_request.content_schema,
+ RadrootsContentSchema::PlainText
+ );
+
let wiki_redirect = event_contract("radroots.wiki.redirect.v1").expect("wiki redirect");
assert_eq!(wiki_redirect.kind, KIND_WIKI_REDIRECT);
+ assert_eq!(
+ wiki_redirect.stability,
+ RadrootsEventStability::Experimental
+ );
assert_eq!(wiki_redirect.content_schema, RadrootsContentSchema::Empty);
for id in [
@@ -3603,6 +3694,7 @@ mod tests {
"radroots.knowledge.contribution_attestation.v1",
] {
let contract = event_contract(id).expect(id);
+ assert_eq!(contract.stability, RadrootsEventStability::Experimental);
assert_eq!(
event_contract_family(contract),
Some(RadrootsContractFamily::Knowledge)
diff --git a/crates/events_codec/src/manifest.rs b/crates/events_codec/src/manifest.rs
@@ -16,7 +16,7 @@ use radroots_events::contract::{
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
-pub const RADROOTS_KNOWLEDGE_CONTRACT_MANIFEST_SCHEMA_VERSION: u32 = 1;
+pub const RADROOTS_KNOWLEDGE_CONTRACT_MANIFEST_SCHEMA_VERSION: u32 = 2;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RadrootsKnowledgeContractManifest {
@@ -44,6 +44,9 @@ pub struct RadrootsKnowledgeContractManifestEntry {
pub reducers: Vec<String>,
pub codec_support: RadrootsKnowledgeManifestCodecSupport,
pub sdk_builder_support: bool,
+ pub sdk_draft_support: bool,
+ pub wasm_tag_builder_support: bool,
+ pub wasm_verified_decode_support: bool,
pub deprecated: bool,
pub replaced_by: Option<String>,
pub introduced_at: String,
@@ -131,6 +134,7 @@ fn manifest_entry(contract: &RadrootsEventContract) -> RadrootsKnowledgeContract
let standard = kind_contract(contract.kind)
.map(|contract| standard_label(contract.standard))
.unwrap_or("unknown");
+ let mvp_support = mvp_sdk_and_wasm_tag_support(contract.id);
RadrootsKnowledgeContractManifestEntry {
contract_id: contract.id.to_string(),
@@ -158,13 +162,30 @@ fn manifest_entry(contract: &RadrootsEventContract) -> RadrootsKnowledgeContract
verified_decode: true,
verified_decode_requires_nostr: true,
},
- sdk_builder_support: false,
+ sdk_builder_support: mvp_support,
+ sdk_draft_support: mvp_support,
+ wasm_tag_builder_support: mvp_support,
+ wasm_verified_decode_support: true,
deprecated: false,
replaced_by: None,
introduced_at: env!("CARGO_PKG_VERSION").to_string(),
}
}
+fn mvp_sdk_and_wasm_tag_support(contract_id: &str) -> bool {
+ matches!(
+ contract_id,
+ "radroots.wiki.article.v1"
+ | "radroots.wiki.redirect.v1"
+ | "radroots.wiki.merge_request.v1"
+ | "radroots.knowledge.source.v1"
+ | "radroots.knowledge.claim.v1"
+ | "radroots.knowledge.relation.v1"
+ | "radroots.knowledge.review.v1"
+ | "radroots.knowledge.field_report.v1"
+ )
+}
+
fn discriminator_manifest(
discriminator: &RadrootsEventDiscriminator,
) -> RadrootsKnowledgeManifestDiscriminator {
diff --git a/crates/events_codec/tests/knowledge_manifest.rs b/crates/events_codec/tests/knowledge_manifest.rs
@@ -20,13 +20,30 @@ use radroots_events_codec::manifest::{
use radroots_test_fixtures::knowledge::RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS;
const MANIFEST_JSON: &str =
- include_str!("../../../contracts/knowledge/knowledge_event_contract_manifest.v1.json");
+ include_str!("../../../contracts/knowledge/knowledge_event_contract_manifest.v2.json");
const MANIFEST_SHA256: &str =
- include_str!("../../../contracts/knowledge/knowledge_event_contract_manifest.v1.sha256");
+ include_str!("../../../contracts/knowledge/knowledge_event_contract_manifest.v2.sha256");
const REGENPROTO_COMPATIBILITY_VECTOR: &str = include_str!(
"../../../contracts/conformance/vectors/knowledge/regenproto_compatibility.v1.json"
);
+const MVP_SUPPORT_CONTRACT_IDS: &[&str] = &[
+ "radroots.wiki.article.v1",
+ "radroots.wiki.redirect.v1",
+ "radroots.wiki.merge_request.v1",
+ "radroots.knowledge.source.v1",
+ "radroots.knowledge.claim.v1",
+ "radroots.knowledge.relation.v1",
+ "radroots.knowledge.review.v1",
+ "radroots.knowledge.field_report.v1",
+];
+
+const BETA_CONTRACT_IDS: &[&str] = &[
+ "radroots.knowledge.evidence_bounty.v1",
+ "radroots.knowledge.change_proposal.v1",
+ "radroots.knowledge.contribution_attestation.v1",
+];
+
#[test]
fn knowledge_manifest_is_deterministic_and_matches_artifacts() {
let first = contract_manifest_json().unwrap();
@@ -72,7 +89,7 @@ fn knowledge_manifest_covers_required_fields_for_every_contract() {
assert!(contract.kind > 0);
assert!(!contract.class.trim().is_empty());
assert!(!contract.standard.trim().is_empty());
- assert!(!contract.stability.trim().is_empty());
+ assert_eq!(contract.stability, "experimental");
assert!(!contract.privacy.trim().is_empty());
assert!(!contract.content_schema.trim().is_empty());
assert!(!contract.payload_type.trim().is_empty());
@@ -84,11 +101,41 @@ fn knowledge_manifest_covers_required_fields_for_every_contract() {
assert!(contract.codec_support.encode);
assert!(contract.codec_support.decode);
assert!(contract.codec_support.contract_validation);
+ assert!(contract.wasm_verified_decode_support);
assert!(!contract.deprecated);
assert!(contract.replaced_by.is_none());
assert!(!contract.introduced_at.trim().is_empty());
}
+ for contract_id in MVP_SUPPORT_CONTRACT_IDS {
+ let contract = manifest
+ .contracts
+ .iter()
+ .find(|contract| contract.contract_id == *contract_id)
+ .unwrap();
+ assert!(contract.sdk_builder_support, "{contract_id}");
+ assert!(contract.sdk_draft_support, "{contract_id}");
+ assert!(contract.wasm_tag_builder_support, "{contract_id}");
+ }
+
+ for contract_id in BETA_CONTRACT_IDS {
+ let contract = manifest
+ .contracts
+ .iter()
+ .find(|contract| contract.contract_id == *contract_id)
+ .unwrap();
+ assert!(!contract.sdk_builder_support, "{contract_id}");
+ assert!(!contract.sdk_draft_support, "{contract_id}");
+ assert!(!contract.wasm_tag_builder_support, "{contract_id}");
+ }
+
+ let merge_request = manifest
+ .contracts
+ .iter()
+ .find(|contract| contract.contract_id == "radroots.wiki.merge_request.v1")
+ .unwrap();
+ assert_eq!(merge_request.content_schema, "plain_text");
+
let manifest_contracts = manifest
.contracts
.iter()
diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs
@@ -13,11 +13,28 @@ const ROOT_RELEASE_POLICY_RELATIVE: &str =
const CONFORMANCE_ROOT_RELATIVE: &str = "contracts/conformance";
const CONFORMANCE_SCHEMA_RELATIVE: &str = "contracts/conformance/schema/vector.schema.json";
const KNOWLEDGE_MANIFEST_RELATIVE: &str =
- "contracts/knowledge/knowledge_event_contract_manifest.v1.json";
+ "contracts/knowledge/knowledge_event_contract_manifest.v2.json";
const KNOWLEDGE_MANIFEST_SHA256_RELATIVE: &str =
- "contracts/knowledge/knowledge_event_contract_manifest.v1.sha256";
+ "contracts/knowledge/knowledge_event_contract_manifest.v2.sha256";
+const KNOWLEDGE_MANIFEST_AND_DECODE_RELATIVE: &str =
+ "contracts/conformance/vectors/knowledge/manifest_and_decode.v1.json";
const KNOWLEDGE_REGENPROTO_COMPATIBILITY_RELATIVE: &str =
"contracts/conformance/vectors/knowledge/regenproto_compatibility.v1.json";
+const KNOWLEDGE_MVP_SUPPORT_CONTRACT_IDS: [&str; 8] = [
+ "radroots.wiki.article.v1",
+ "radroots.wiki.redirect.v1",
+ "radroots.wiki.merge_request.v1",
+ "radroots.knowledge.source.v1",
+ "radroots.knowledge.claim.v1",
+ "radroots.knowledge.relation.v1",
+ "radroots.knowledge.review.v1",
+ "radroots.knowledge.field_report.v1",
+];
+const KNOWLEDGE_BETA_CONTRACT_IDS: [&str; 3] = [
+ "radroots.knowledge.evidence_bounty.v1",
+ "radroots.knowledge.change_proposal.v1",
+ "radroots.knowledge.contribution_attestation.v1",
+];
const RELEASE_POLICY_ENV: &str = "RADROOTS_MOUNTED_RUST_CRATE_PUBLISH_POLICY";
const EVENT_BOUNDARY_MATRIX_ENV: &str = "RADROOTS_EVENT_BOUNDARY_MATRIX";
const COVERAGE_REQUIRED_THRESHOLD: f64 = 100.0;
@@ -2099,14 +2116,133 @@ pub fn validate_knowledge_contract_manifest(workspace_root: &Path) -> Result<(),
));
}
+ validate_knowledge_manifest_witnesses(workspace_root, &actual_json)?;
+
let bundle = load_contract_bundle(workspace_root)?;
validate_conformance_vector_file(
+ &workspace_root.join(KNOWLEDGE_MANIFEST_AND_DECODE_RELATIVE),
+ &bundle.manifest.contract.version,
+ )?;
+ validate_conformance_vector_file(
&workspace_root.join(KNOWLEDGE_REGENPROTO_COMPATIBILITY_RELATIVE),
&bundle.manifest.contract.version,
)?;
Ok(())
}
+fn validate_knowledge_manifest_witnesses(
+ workspace_root: &Path,
+ actual_json: &str,
+) -> Result<(), String> {
+ for relative in legacy_knowledge_manifest_relatives() {
+ let path = workspace_root.join(&relative);
+ if path.exists() {
+ return Err(format!(
+ "stale knowledge manifest artifact remains at {}",
+ relative
+ ));
+ }
+ }
+
+ let value = serde_json::from_str::<Value>(actual_json)
+ .map_err(|error| format!("parse knowledge manifest JSON: {error}"))?;
+ if value.get("schema_version").and_then(Value::as_u64) != Some(2) {
+ return Err("knowledge manifest schema_version must be 2".to_string());
+ }
+ let contracts = value
+ .get("contracts")
+ .and_then(Value::as_array)
+ .ok_or_else(|| "knowledge manifest contracts must be an array".to_string())?;
+ let mut previous_id: Option<String> = None;
+ let mut ids = BTreeSet::new();
+
+ for contract in contracts {
+ let contract_id = contract
+ .get("contract_id")
+ .and_then(Value::as_str)
+ .ok_or_else(|| "knowledge manifest entry missing contract_id".to_string())?;
+ if let Some(previous) = previous_id.as_deref() {
+ if previous > contract_id {
+ return Err(
+ "knowledge manifest contracts must be sorted by contract_id".to_string()
+ );
+ }
+ }
+ previous_id = Some(contract_id.to_string());
+ if !ids.insert(contract_id.to_string()) {
+ return Err(format!(
+ "knowledge manifest has duplicate contract id {contract_id}"
+ ));
+ }
+
+ if contract.get("stability").and_then(Value::as_str) != Some("experimental") {
+ return Err(format!(
+ "knowledge manifest contract {contract_id} must be experimental"
+ ));
+ }
+ if contract_id == "radroots.wiki.merge_request.v1"
+ && contract.get("content_schema").and_then(Value::as_str) != Some("plain_text")
+ {
+ return Err(
+ "wiki merge request manifest content_schema must be plain_text".to_string(),
+ );
+ }
+
+ let sdk_builder_support = manifest_bool_field(contract, "sdk_builder_support")?;
+ let sdk_draft_support = manifest_bool_field(contract, "sdk_draft_support")?;
+ let wasm_tag_builder_support = manifest_bool_field(contract, "wasm_tag_builder_support")?;
+ let wasm_verified_decode_support =
+ manifest_bool_field(contract, "wasm_verified_decode_support")?;
+
+ if KNOWLEDGE_MVP_SUPPORT_CONTRACT_IDS.contains(&contract_id) {
+ if !sdk_builder_support || !sdk_draft_support || !wasm_tag_builder_support {
+ return Err(format!(
+ "knowledge manifest MVP contract {contract_id} must report SDK and WASM tag support"
+ ));
+ }
+ }
+ if KNOWLEDGE_BETA_CONTRACT_IDS.contains(&contract_id)
+ && (sdk_builder_support || sdk_draft_support || wasm_tag_builder_support)
+ {
+ return Err(format!(
+ "knowledge manifest beta contract {contract_id} must not overclaim builder support"
+ ));
+ }
+ if !wasm_verified_decode_support {
+ return Err(format!(
+ "knowledge manifest contract {contract_id} must report WASM verified decode support"
+ ));
+ }
+ }
+
+ for contract_id in KNOWLEDGE_MVP_SUPPORT_CONTRACT_IDS
+ .iter()
+ .chain(KNOWLEDGE_BETA_CONTRACT_IDS.iter())
+ {
+ if !ids.contains(*contract_id) {
+ return Err(format!(
+ "knowledge manifest missing required contract {contract_id}"
+ ));
+ }
+ }
+
+ Ok(())
+}
+
+fn legacy_knowledge_manifest_relatives() -> [String; 2] {
+ [
+ KNOWLEDGE_MANIFEST_RELATIVE.replace(".v2.", ".v1."),
+ KNOWLEDGE_MANIFEST_SHA256_RELATIVE.replace(".v2.", ".v1."),
+ ]
+}
+
+fn manifest_bool_field(contract: &Value, field: &str) -> Result<bool, String> {
+ contract
+ .get(field)
+ .and_then(Value::as_bool)
+ .ok_or_else(|| format!("knowledge manifest entry missing boolean field {field}"))
+}
+
#[cfg(not(test))]
fn validate_missing_conformance_vectors(
_workspace_root: &Path,