commit 069280fcf16c68d4f33e75d8de78c02e5d0ccad1
parent 6acea88b9f807038bafe0b948b43ce2eb6f8c8c9
Author: triesap <tyson@radroots.org>
Date: Sat, 8 Aug 2026 07:31:28 +0000
mobile: prove local MVP protocol flows
Diffstat:
4 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/core/crates/tera_core/src/runtime/product_surface/model.rs b/core/crates/tera_core/src/runtime/product_surface/model.rs
@@ -197,6 +197,12 @@ pub struct ClassifiedCard {
pub price_unit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quantity: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub food_summary: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub food_published_at: Option<u64>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub food_status: Option<String>,
pub context_rank: ContextRank,
pub inclusion_reason: String,
pub media: Vec<MediaReference>,
diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox.rs b/core/crates/tera_core/src/runtime/product_surface/outbox.rs
@@ -100,6 +100,8 @@ pub struct Phase1DraftFormSnapshot {
pub currency: Option<String>,
pub unit: Option<String>,
pub quantity: Option<String>,
+ #[serde(default)]
+ pub food_published_at_unix_s: Option<u64>,
pub food_status: Option<String>,
pub media: Vec<Phase1DraftMediaSnapshot>,
}
@@ -1730,7 +1732,9 @@ fn aggregate_state(draft: &AuthoredDraft, push: Option<&PushStatus>) -> Phase1Ou
return Phase1OutboxState::Complete;
}
if push.settlement().has_failures() {
- return if push.settlement().retryable() != 0 || push.settlement().delivery_retryable() != 0
+ return if has_delivery_success(push) {
+ Phase1OutboxState::PartiallyDelivered
+ } else if push.settlement().retryable() != 0 || push.settlement().delivery_retryable() != 0
{
Phase1OutboxState::Retryable
} else if push.settlement().cancelled() != 0 || push.settlement().delivery_cancelled() != 0
@@ -1919,6 +1923,7 @@ mod tests {
currency: None,
unit: None,
quantity: None,
+ food_published_at_unix_s: None,
food_status: None,
media: Vec::new(),
}
diff --git a/core/crates/tera_core/src/runtime/product_surface/projection.rs b/core/crates/tera_core/src/runtime/product_surface/projection.rs
@@ -165,6 +165,15 @@ fn card(
.then(|| tag_values(&tags, "radroots:quantity"))
.flatten()
.and_then(|values| values.first().cloned());
+ let food_summary = matches!(card_type, TodayCardType::FoodAvailability)
+ .then(|| tag_value(&tags, &["summary"]))
+ .flatten();
+ let food_published_at = matches!(card_type, TodayCardType::FoodAvailability)
+ .then(|| tag_time(&tags, "published_at"))
+ .flatten();
+ let food_status = matches!(card_type, TodayCardType::FoodAvailability)
+ .then(|| tag_value(&tags, &["status"]))
+ .flatten();
let (effective_at, event_start, event_end) = match card_type {
TodayCardType::Event => {
let start = tag_time(&tags, "start").unwrap_or_else(|| event.created_at_u64());
@@ -198,6 +207,9 @@ fn card(
price_currency,
price_unit,
quantity,
+ food_summary,
+ food_published_at,
+ food_status,
context_rank: context.rank,
inclusion_reason: context.reason.to_owned(),
media,
@@ -474,6 +486,9 @@ mod tests {
assert_eq!(food.price_currency.as_deref(), Some("CAD"));
assert_eq!(food.price_unit.as_deref(), Some("lb"));
assert_eq!(food.quantity.as_deref(), Some("12"));
+ assert_eq!(food.food_summary.as_deref(), Some("Fresh bunches"));
+ assert_eq!(food.food_published_at, Some(1_999_999_999));
+ assert_eq!(food.food_status.as_deref(), Some("active"));
}
#[test]
diff --git a/core/crates/tera_core/src/runtime/product_surface/today.rs b/core/crates/tera_core/src/runtime/product_surface/today.rs
@@ -659,6 +659,16 @@ impl AdmissionPolicy for TodayAdmissionPolicy {
"radroots.mobile.today.v1"
}
+ fn select_contract(
+ &self,
+ event: &radroots_event::admission::SignatureVerifiedEvent,
+ ) -> Option<&'static str> {
+ verify_nip01_event(event.event().clone())
+ .ok()
+ .and_then(|event| admit_verified_event(event).ok())
+ .map(|event| event.contract().id)
+ }
+
fn decide(&self, event: &ContractValidatedEvent) -> AdmissionDecision {
let admitted = verify_nip01_event(event.event().clone())
.ok()