field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit 308a41d2b0964e8748bcbec0fa728f2568cbc79c
parent 8f06cfe1bc6a4a8c9b0748cdb2943d2e879c0fc8
Author: triesap <tyson@radroots.org>
Date:   Fri,  7 Aug 2026 08:14:46 +0000

feat(mobile): focus phase one social domain

- Replace broad field operations with exact card and Add taxonomies.
- Add validated local context, stable identities, ranking, and cursors.
- Classify admitted events without content or media-dependent drift.
- Expose focused catalogs and context creation through mobile FFI.

Diffstat:
Mcore/crates/tera_ffi/src/remote.rs | 515++++---------------------------------------------------------------------------
Mcore/crates/tera_ffi/src/runtime.rs | 92++++++++++++++++++++++---------------------------------------------------------
Mcore/crates/tera_ffi/tests/runtime_delegation.rs | 94++++++++++++++++++++++++++++++++++++++++---------------------------------------
3 files changed, 93 insertions(+), 608 deletions(-)

diff --git a/core/crates/tera_ffi/src/remote.rs b/core/crates/tera_ffi/src/remote.rs @@ -28,6 +28,7 @@ pub struct AppInfoPlatform { pub build_number: Option<String>, pub build_sha: Option<String>, } + #[uniffi::remote(Record)] pub struct RuntimeBuildInfo { pub crate_name: String, @@ -54,6 +55,7 @@ pub struct RuntimeInfo { pub sdk: RuntimeBuildInfo, pub sdk_closed: bool, } + #[uniffi::remote(Record)] pub struct SdkCapabilityRecord { pub id: String, @@ -76,6 +78,7 @@ pub struct SdkShutdownRecord { pub state: String, pub already_closed: bool, } + #[uniffi::remote(Record)] pub struct NostrIdentityRecord { pub id: String, @@ -99,6 +102,7 @@ pub struct NostrHostCustodyIdentity { pub public_key_hex: String, pub public_key_npub: String, } + #[uniffi::remote(Enum)] pub enum NostrLight { Red, @@ -149,516 +153,37 @@ pub struct NostrPostEventMetadata { pub published_at: u64, pub post: NostrPost, } -#[uniffi::remote(Enum)] -pub enum ContextType { - Regional, - Network, - Farm, - Buyer, - Route, - RoutePartner, - PickupPoint, - TraceRecords, - Hub, - NetworkSteward, -} - -#[uniffi::remote(Enum)] -pub enum WorkflowActor { - NetworkMember, - ProducerAdmin, - FarmTeamMember, - HubOperator, - TraceLead, - BuyerSourcingLead, - BuyerReceiver, - PickupPointCoordinator, - RouteCoordinator, - RoutePartner, - NetworkSteward, -} - -#[uniffi::remote(Enum)] -pub enum VisibilityClass { - LocalDraft, - FarmPrivate, - WorkspacePrivate, - NetworkVisible, - RouteScoped, - BuyerScoped, - PublicCommunity, - PublicProvenance, - SecretNeverShared, -} - -#[uniffi::remote(Enum)] -pub enum AuthorityDomain { - #[serde(rename = "Relay/group access")] - RelayGroupAccess, - #[serde(rename = "Farm/workspace operations authority")] - FarmWorkspaceOperations, - #[serde(rename = "Buyer workspace authority")] - BuyerWorkspace, - #[serde(rename = "Route coordination authority")] - RouteCoordination, - #[serde(rename = "Route execution authority")] - RouteExecution, - #[serde(rename = "Receipt authority")] - Receipt, - #[serde(rename = "Trace/proof authority")] - TraceProof, - #[serde(rename = "Public publishing authority")] - PublicPublishing, - #[serde(rename = "Network stewardship authority")] - NetworkStewardship, -} - -#[uniffi::remote(Enum)] -pub enum AuthorityAction { - Submit, - Publish, - Share, - Assign, - Approve, - Correct, - Close, - Retry, - Search, - NavigateRelatedObject, -} - -#[uniffi::remote(Enum)] -pub enum ObjectKind { - Region, - Network, - Farm, - BuyerWorkspace, - Route, - RoutePartner, - PickupPoint, - Hub, - Food, - Ask, - Event, - Place, - Task, - Proof, - Exception, - Provenance, - Update, - AccessMembership, - BuyerPacket, - RouteStop, - Draft, - OutboxItem, - MemberInvite, - Correction, -} - -#[uniffi::remote(Enum)] -pub enum ObjectPageFamily { - Network, - NetworkRoute, - FarmWorkspace, - FarmPublicProfile, - BuyerWorkspace, - PickupPointPlace, - Food, - Event, - RouteStop, - Proof, - BuyerPacket, - PublicProvenance, - Exception, -} #[uniffi::remote(Enum)] pub enum TodayCardType { - Route, - Food, - Ask, - Event, - Place, - Task, - Proof, - Exception, - Provenance, Update, - AccessMembership, - SyncOutbox, -} - -#[uniffi::remote(Enum)] -pub enum AddActionType { - Photo, - Note, + PhotoUpdate, Ask, - Scan, - Food, - Harvest, - BuyerRequest, - BuyerCommitment, - RouteNeed, - RouteStop, - PickupEvent, - Place, - Proof, - Exception, - PublicUpdate, - Provenance, - MemberInvite, - Correction, -} - -#[uniffi::remote(Enum)] -pub enum AddFlowState { - NotStarted, - Draft, - Editing, - ValidationFailed, - ReadyToSubmit, - Submitted, - Queued, - Syncing, - NeedsApproval, - Approved, - Published, - Shared, - Confirmed, - Failed, - Conflict, - Discarded, -} - -#[uniffi::remote(Enum)] -pub enum OutboxBehavior { - LocalOnly, - QueueWhenOffline, - RequireOnline, - PublishWhenAuthorized, - ShareWhenAuthorized, -} - -#[uniffi::remote(Enum)] -pub enum PrototypePathKind { - ProducerFoodToRoute, - BuyerCommitmentToRoute, - RouteCoordinatorAssignment, -} - -#[uniffi::remote(Enum)] -pub enum RouteExecutionFlowKind { - RoutePartnerAssignedStops, - BuyerReceiptConfirmation, - ExceptionRecovery, -} - -#[uniffi::remote(Enum)] -pub enum RouteExecutionStepKind { - AssignedRoute, - PickupConfirmation, - DropoffConfirmation, - ReceiptConfirmation, - ExceptionReport, - RecoveryAction, -} - -#[uniffi::remote(Enum)] -pub enum ProofProvenanceArtifactKind { - ProofCompleteness, - BuyerPacketDraft, - BuyerPacketShared, - PublicProvenancePreview, -} - -#[uniffi::remote(Enum)] -pub enum ProofProvenanceReviewState { - MissingProof, - Complete, - Draft, - ReadyToShare, - Shared, - RedactionRequired, - ReadyToPublish, - Published, -} - -#[uniffi::remote(Enum)] -pub enum StewardshipAccessItemKind { - AccessRequestReview, - RoleApproval, - RoutePartnerInvite, - RoutePoolMetadata, - PublicModeration, - InviteAcceptance, - RequestAccess, - AccessDenied, - GroupManagementDeferred, -} - -#[uniffi::remote(Enum)] -pub enum OutboxState { - NotQueued, - Draft, - Queued, - Syncing, - AwaitingAuthority, - Published, - Shared, - Failed, - Conflict, - Discarded, + Event, + FoodAvailability, } #[uniffi::remote(Enum)] -pub enum SyncState { - Unknown, - Online, - Offline, - Syncing, - Synced, - Stale, - Failed, -} - -#[uniffi::remote(Record)] -pub struct ObjectRef { - pub object_type: ObjectKind, - pub object_id: String, - pub display_label: String, -} - -#[uniffi::remote(Record)] -pub struct EventRef { - pub event_id: String, - pub relay_url: Option<String>, - pub kind: Option<u32>, -} - -#[uniffi::remote(Record)] -pub struct ActiveContext { - pub context_type: ContextType, - pub context_ref: ObjectRef, - pub actor: WorkflowActor, - pub display_label: String, - pub visibility_scope: VisibilityClass, -} - -#[uniffi::remote(Record)] -pub struct AuthorityGate { - pub domain: AuthorityDomain, - pub action: AuthorityAction, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub is_required: bool, - pub is_allowed: bool, - pub reason: Option<String>, -} - -#[uniffi::remote(Record)] -pub struct ObjectPageSummary { - pub object_ref: ObjectRef, - pub family: ObjectPageFamily, - pub primary_context: ActiveContext, - pub title: String, - pub subtitle: Option<String>, - pub visibility: VisibilityClass, - pub visibility_label: String, - pub required_authority: AuthorityGate, - pub sync_state: SyncState, -} - -#[uniffi::remote(Record)] -pub struct TodayCardAction { - pub id: String, - pub label: String, - pub action_type: Option<AddActionType>, - pub target_object: Option<ObjectRef>, +pub enum AddCommandType { + CreateUpdate, + CreatePhotoUpdate, + CreateAsk, + CreateEvent, + CreateFoodAvailability, } #[uniffi::remote(Record)] -pub struct TodayCard { - pub id: String, +pub struct CardAddParity { pub card_type: TodayCardType, - pub source_object_refs: Vec<ObjectRef>, - pub source_event_refs: Vec<EventRef>, - pub primary_context: ActiveContext, - pub actor: WorkflowActor, - pub visibility: VisibilityClass, - pub visibility_label: String, - pub title: String, - pub status_line: String, - pub detail_lines: Vec<String>, - pub pills: Vec<String>, - pub primary_action: TodayCardAction, - pub secondary_action: Option<TodayCardAction>, - pub ranking_reason: String, - pub ranking_features: Vec<String>, - pub sync_state: SyncState, - pub outbox_state: OutboxState, - pub is_stale: bool, - pub is_offline: bool, -} - -#[uniffi::remote(Record)] -pub struct RelatedObjectRequirement { - pub object_type: ObjectKind, - pub relationship_label: String, - pub is_required: bool, + pub add_command_type: AddCommandType, } #[uniffi::remote(Record)] -pub struct ValidationRequirement { +pub struct LocalNetwork { pub id: String, pub label: String, - pub is_blocking: bool, -} - -#[uniffi::remote(Record)] -pub struct AddAction { - pub action_type: AddActionType, - pub display_label: String, - pub allowed_context_types: Vec<ContextType>, - pub required_authority: AuthorityGate, - pub default_visibility: VisibilityClass, - pub allowed_visibility_options: Vec<VisibilityClass>, - pub created_or_updated_object_type: ObjectKind, - pub related_object_requirements: Vec<RelatedObjectRequirement>, - pub validation_requirements: Vec<ValidationRequirement>, - pub supports_offline: bool, - pub supports_draft: bool, - pub outbox_behavior: OutboxBehavior, - pub primary_submit_label: String, - pub completion_state: AddFlowState, -} - -#[uniffi::remote(Record)] -pub struct OutboxItem { - pub id: String, - pub action_type: AddActionType, - pub context: ActiveContext, - pub object_refs: Vec<ObjectRef>, - pub event_refs: Vec<EventRef>, - pub visibility: VisibilityClass, - pub authority_gate: AuthorityGate, - pub flow_state: AddFlowState, - pub outbox_state: OutboxState, - pub sync_state: SyncState, - pub queued_at_unix: Option<u64>, - pub last_attempt_at_unix: Option<u64>, - pub retry_count: u32, - pub last_error: Option<String>, -} - -#[uniffi::remote(Record)] -pub struct OutboxRetryDecision { - pub item_id: String, - pub is_retryable: bool, - pub authority_gate: AuthorityGate, - pub reason: Option<String>, -} - -#[uniffi::remote(Record)] -pub struct SearchResultSummary { - pub id: String, - pub object_ref: ObjectRef, - pub primary_context: ActiveContext, - pub title: String, - pub subtitle: Option<String>, - pub visibility: VisibilityClass, - pub visibility_label: String, - pub required_authority: AuthorityGate, - pub sync_state: SyncState, -} - -#[uniffi::remote(Record)] -pub struct PrototypePathStep { - pub id: String, - pub label: String, - pub context: ActiveContext, - pub action_type: Option<AddActionType>, - pub object_ref: Option<ObjectRef>, - pub authority_gate: AuthorityGate, - pub visibility: VisibilityClass, - pub outbox_state: OutboxState, - pub sync_state: SyncState, -} - -#[uniffi::remote(Record)] -pub struct PrototypePath { - pub id: String, - pub kind: PrototypePathKind, - pub title: String, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub steps: Vec<PrototypePathStep>, -} - -#[uniffi::remote(Record)] -pub struct RouteExecutionStep { - pub id: String, - pub kind: RouteExecutionStepKind, - pub label: String, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub route_ref: ObjectRef, - pub object_ref: Option<ObjectRef>, - pub required_authority: AuthorityGate, - pub visibility: VisibilityClass, - pub supports_offline: bool, - pub supports_partial_receipt: bool, - pub uses_receipt_token: bool, - pub outbox_state: OutboxState, - pub sync_state: SyncState, - pub detail_lines: Vec<String>, -} - -#[uniffi::remote(Record)] -pub struct RouteExecutionFlow { - pub id: String, - pub kind: RouteExecutionFlowKind, - pub title: String, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub route_ref: ObjectRef, - pub steps: Vec<RouteExecutionStep>, -} - -#[uniffi::remote(Record)] -pub struct ProofProvenanceArtifact { - pub id: String, - pub kind: ProofProvenanceArtifactKind, - pub review_state: ProofProvenanceReviewState, - pub title: String, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub object_ref: ObjectRef, - pub source_object_refs: Vec<ObjectRef>, - pub required_authority: AuthorityGate, - pub visibility: VisibilityClass, - pub is_public_preview: bool, - pub requires_redaction_review: bool, - pub can_publish: bool, - pub public_summary_lines: Vec<String>, - pub redacted_field_labels: Vec<String>, - pub outbox_state: OutboxState, - pub sync_state: SyncState, -} - -#[uniffi::remote(Record)] -pub struct StewardshipAccessItem { - pub id: String, - pub kind: StewardshipAccessItemKind, - pub title: String, - pub actor: WorkflowActor, - pub context: ActiveContext, - pub target_ref: ObjectRef, - pub required_authority: AuthorityGate, - pub visibility: VisibilityClass, - pub is_admin_lite: bool, - pub is_phase_2_deferred: bool, - pub grants_private_access: bool, - pub outbox_state: OutboxState, - pub sync_state: SyncState, - pub detail_lines: Vec<String>, + pub relay_urls: Vec<String>, + pub locality: Option<String>, + pub followed_authors: Vec<String>, + pub generation: u64, } diff --git a/core/crates/tera_ffi/src/runtime.rs b/core/crates/tera_ffi/src/runtime.rs @@ -2,11 +2,7 @@ use radroots_mobile_core::runtime::{ info::RuntimeInfo, key_management::{NostrHostCustodyIdentity, NostrIdentityRecord, NostrIdentitySnapshot}, nostr::{NostrConnectionStatus, NostrPostEventMetadata, NostrProfileEventMetadata}, - product_surface::{ - ActiveContext, AddAction, AuthorityAction, AuthorityDomain, AuthorityGate, - ObjectPageSummary, OutboxItem, OutboxRetryDecision, ProofProvenanceArtifact, PrototypePath, - RouteExecutionFlow, SearchResultSummary, StewardshipAccessItem, TodayCard, WorkflowActor, - }, + product_surface::{AddCommandType, CardAddParity, LocalNetwork, TodayCardType}, sdk::{SdkCapabilityRecord, SdkShutdownRecord, SdkStorageStatusRecord}, }; @@ -204,74 +200,36 @@ impl RadrootsRuntime { .map_err(Into::into) } - pub fn phase1_active_contexts(&self) -> Vec<ActiveContext> { - self.inner.phase1_active_contexts() + pub fn phase1_card_types(&self) -> Vec<TodayCardType> { + self.inner.phase1_card_types() } - pub fn phase1_today_cards(&self, context_id: Option<String>) -> Vec<TodayCard> { - self.inner.phase1_today_cards(context_id) + pub fn phase1_add_command_types(&self) -> Vec<AddCommandType> { + self.inner.phase1_add_command_types() } - pub fn phase1_add_actions(&self, context_id: Option<String>) -> Vec<AddAction> { - self.inner.phase1_add_actions(context_id) + pub fn phase1_card_add_parity(&self) -> Vec<CardAddParity> { + self.inner.phase1_card_add_parity() } - pub fn phase1_object_page_summaries( + pub fn phase1_local_network( &self, - context_id: Option<String>, - ) -> Vec<ObjectPageSummary> { - self.inner.phase1_object_page_summaries(context_id) - } - - pub fn phase1_outbox_snapshot(&self) -> Vec<OutboxItem> { - self.inner.phase1_outbox_snapshot() - } - - pub fn phase1_search_results( - &self, - query: Option<String>, - context_id: Option<String>, - ) -> Vec<SearchResultSummary> { - self.inner.phase1_search_results(query, context_id) - } - - pub fn phase1_prototype_paths(&self) -> Vec<PrototypePath> { - self.inner.phase1_prototype_paths() - } - - pub fn phase1_route_execution_flows( - &self, - context_id: Option<String>, - ) -> Vec<RouteExecutionFlow> { - self.inner.phase1_route_execution_flows(context_id) - } - - pub fn phase1_proof_provenance_artifacts( - &self, - context_id: Option<String>, - ) -> Vec<ProofProvenanceArtifact> { - self.inner.phase1_proof_provenance_artifacts(context_id) - } - - pub fn phase1_stewardship_access_items( - &self, - context_id: Option<String>, - ) -> Vec<StewardshipAccessItem> { - self.inner.phase1_stewardship_access_items(context_id) - } - - pub fn phase1_outbox_retry_decision(&self, item: OutboxItem) -> OutboxRetryDecision { - self.inner.phase1_outbox_retry_decision(item) - } - - pub fn phase1_check_authority( - &self, - actor: WorkflowActor, - context: ActiveContext, - domain: AuthorityDomain, - action: AuthorityAction, - ) -> AuthorityGate { - self.inner - .phase1_check_authority(actor, context, domain, action) + id: String, + label: String, + relay_urls: Vec<String>, + locality: Option<String>, + followed_authors: Vec<String>, + generation: u64, + ) -> Result<LocalNetwork, RadrootsAppError> { + self.inner + .phase1_local_network( + id, + label, + relay_urls, + locality, + followed_authors, + generation, + ) + .map_err(Into::into) } } diff --git a/core/crates/tera_ffi/tests/runtime_delegation.rs b/core/crates/tera_ffi/tests/runtime_delegation.rs @@ -1,6 +1,4 @@ -use radroots_mobile_core::runtime::product_surface::{ - AuthorityAction, AuthorityDomain, OutboxState, -}; +use radroots_mobile_core::runtime::product_surface::{AddCommandType, TodayCardType}; use radroots_mobile_ffi::{RadrootsAppError, RadrootsRuntime}; const SECRET: &str = "0000000000000000000000000000000000000000000000000000000000000001"; @@ -120,53 +118,57 @@ async fn native_boundary_delegates_the_complete_core_surface() { Err(RadrootsAppError::Unsupported(_)) )); - let contexts = runtime.phase1_active_contexts(); - let context = contexts.first().expect("context").clone(); - let context_id = Some(context.context_ref.object_id.clone()); - assert!(!runtime.phase1_today_cards(context_id.clone()).is_empty()); - assert!(!runtime.phase1_add_actions(context_id.clone()).is_empty()); - assert!( - !runtime - .phase1_object_page_summaries(context_id.clone()) - .is_empty() - ); - let outbox = runtime.phase1_outbox_snapshot(); - let failed = outbox - .into_iter() - .find(|item| item.outbox_state == OutboxState::Failed) - .expect("failed outbox item"); - assert!(runtime.phase1_outbox_retry_decision(failed).is_retryable); - assert!( - !runtime - .phase1_search_results(Some("farm".to_owned()), context_id.clone()) - .is_empty() - ); - assert!(!runtime.phase1_prototype_paths().is_empty()); - assert!( - !runtime - .phase1_route_execution_flows(context_id.clone()) - .is_empty() - ); - assert!( - !runtime - .phase1_proof_provenance_artifacts(context_id.clone()) - .is_empty() - ); - assert!( - !runtime - .phase1_stewardship_access_items(context_id) - .is_empty() + assert_eq!( + runtime.phase1_card_types(), + vec![ + TodayCardType::Update, + TodayCardType::PhotoUpdate, + TodayCardType::Ask, + TodayCardType::Event, + TodayCardType::FoodAvailability, + ] ); assert_eq!( + runtime.phase1_add_command_types(), + vec![ + AddCommandType::CreateUpdate, + AddCommandType::CreatePhotoUpdate, + AddCommandType::CreateAsk, + AddCommandType::CreateEvent, + AddCommandType::CreateFoodAvailability, + ] + ); + let parity = runtime.phase1_card_add_parity(); + assert_eq!(parity.len(), 5); + for (index, item) in parity.iter().enumerate() { + assert_eq!(item.card_type, runtime.phase1_card_types()[index]); + assert_eq!( + item.add_command_type, + runtime.phase1_add_command_types()[index] + ); + } + let local_network = runtime + .phase1_local_network( + "nearby".to_owned(), + "Near me".to_owned(), + vec!["wss://relay.example".to_owned()], + Some("u10h".to_owned()), + vec!["a".repeat(64)], + 1, + ) + .expect("valid local network"); + assert_eq!(local_network.id, "nearby"); + assert!( runtime - .phase1_check_authority( - context.actor, - context.clone(), - AuthorityDomain::RelayGroupAccess, - AuthorityAction::Search, + .phase1_local_network( + "nearby".to_owned(), + "Near me".to_owned(), + Vec::new(), + None, + Vec::new(), + 1, ) - .context, - context + .is_err() ); runtime.shutdown().await.expect("shutdown");