commit 802e7b1636fd17aa28a3ce3a83e1d17a070ca182
parent af238499ecca19224f03372a7ff18a2b35cbc04c
Author: triesap <tyson@radroots.org>
Date: Sat, 23 May 2026 09:34:33 +0000
runtime: mark unresolved app local records non-exportable
- remove npub fallback from shared local work pubkey fields
- omit listing addresses until a hex public key is resolved
- mark app-authored local work exportability in payloads
- cover resolved and unresolved producer identity states
Diffstat:
1 file changed, 141 insertions(+), 22 deletions(-)
diff --git a/crates/launchers/desktop/src/runtime.rs b/crates/launchers/desktop/src/runtime.rs
@@ -3300,6 +3300,7 @@ impl DesktopAppRuntimeState {
let timestamp = current_runtime_time_ms()?;
let farm_d_tag = d_tag_from_uuid(saved_farm.farm_id.as_uuid());
let owner_pubkey = self.local_events_owner_pubkey(account);
+ let exportability = app_local_work_exportability(owner_pubkey.as_deref());
let delivery_method = projection
.draft
.order_methods
@@ -3309,6 +3310,7 @@ impl DesktopAppRuntimeState {
let payload = json!({
"record_kind": "farm_config_v1",
"scope": "app",
+ "exportability": exportability,
"document": {
"version": 1,
"selection": {
@@ -3343,7 +3345,7 @@ impl DesktopAppRuntimeState {
created_at_ms: timestamp,
inserted_at_ms: timestamp,
owner_account_id: Some(account.account.account_id.clone()),
- owner_pubkey: Some(owner_pubkey),
+ owner_pubkey,
farm_id: Some(farm_d_tag),
listing_addr: None,
local_work_json: Some(payload),
@@ -3387,7 +3389,10 @@ impl DesktopAppRuntimeState {
let farm_d_tag = d_tag_from_uuid(farm_id.as_uuid());
let listing_d_tag = d_tag_from_uuid(product_id.as_uuid());
let owner_pubkey = self.local_events_owner_pubkey(account);
- let listing_addr = format!("30402:{owner_pubkey}:{listing_d_tag}");
+ let listing_addr = owner_pubkey
+ .as_ref()
+ .map(|pubkey| format!("30402:{pubkey}:{listing_d_tag}"));
+ let exportability = app_local_work_exportability(owner_pubkey.as_deref());
let farm_setup = self.state_store.farm_setup_projection();
let delivery_method = farm_setup
.draft
@@ -3416,6 +3421,7 @@ impl DesktopAppRuntimeState {
.unwrap_or_else(|| "0".to_owned());
let payload = json!({
"record_kind": "listing_draft_v1",
+ "exportability": exportability,
"document": {
"version": 1,
"kind": "listing_draft_v1",
@@ -3425,7 +3431,7 @@ impl DesktopAppRuntimeState {
},
"seller_actor": {
"account_id": account.account.account_id,
- "pubkey": owner_pubkey,
+ "pubkey": owner_pubkey.as_deref(),
"source": "farm_config",
},
"product": {
@@ -3466,9 +3472,9 @@ impl DesktopAppRuntimeState {
created_at_ms: timestamp,
inserted_at_ms: timestamp,
owner_account_id: Some(account.account.account_id.clone()),
- owner_pubkey: Some(owner_pubkey),
+ owner_pubkey,
farm_id: Some(farm_d_tag),
- listing_addr: Some(listing_addr),
+ listing_addr,
local_work_json: Some(payload),
event_id: None,
event_kind: None,
@@ -3528,9 +3534,9 @@ impl DesktopAppRuntimeState {
fn local_events_owner_pubkey(
&self,
account: &radroots_app_models::SelectedAccountProjection,
- ) -> String {
+ ) -> Option<String> {
if is_hex_64(account.account.account_id.as_str()) {
- return account.account.account_id.clone();
+ return Some(account.account.account_id.clone());
}
self.accounts_manager
.as_ref()
@@ -3541,7 +3547,6 @@ impl DesktopAppRuntimeState {
})
.map(|record| record.public_identity.public_key_hex)
.filter(|pubkey| is_hex_64(pubkey))
- .unwrap_or_else(|| account.account.npub.clone())
}
fn refresh_selected_account_context_after_local_events(
@@ -3941,6 +3946,18 @@ fn is_hex_64(value: &str) -> bool {
value.len() == 64 && value.bytes().all(|byte| byte.is_ascii_hexdigit())
}
+fn app_local_work_exportability(owner_pubkey: Option<&str>) -> serde_json::Value {
+ match owner_pubkey {
+ Some(_) => json!({
+ "state": "exportable"
+ }),
+ None => json!({
+ "state": "identity_unresolved",
+ "reason": "canonical_hex_pubkey_required"
+ }),
+ }
+}
+
fn load_selected_account_context(
sqlite_store: &AppSqliteStore,
identity_projection: &AppIdentityProjection,
@@ -5233,20 +5250,22 @@ mod tests {
AppSharedAccountsPaths, SHARED_ACCOUNTS_STORE_FILE_NAME, SHARED_IDENTITY_FILE_NAME,
};
use radroots_app_models::{
- AccountSurfaceActivationProjection, ActiveSurface, AppActivityKind, AppStartupGate,
- BlackoutPeriodId, BlackoutPeriodRecord, BuyerCheckoutDraft, FarmId,
- FarmOperatingRulesRecord, FarmOrderMethod, FarmProfileRecord, FarmReadiness,
- FarmReadinessBlocker, FarmSetupDraft, FarmSetupProjection, FarmSummary,
- FarmerActivationProjection, FarmerSection, FulfillmentWindowId, FulfillmentWindowRecord,
- LoggedOutStartupProjection, OrderId, OrderStatus, OrdersFilter, PackDayBatchPrintArtifact,
- PackDayBatchPrintFailureKind, PackDayBatchPrintStatus, PackDayExportInstanceId,
- PackDayExportStatus, PackDayHostHandoffKind, PackDayHostHandoffStatus, PackDayPackListRow,
+ AccountCustody, AccountSummary, AccountSurfaceActivationProjection, ActiveSurface,
+ AppActivityKind, AppIdentityProjection, AppStartupGate, BlackoutPeriodId,
+ BlackoutPeriodRecord, BuyerCheckoutDraft, FarmId, FarmOperatingRulesRecord,
+ FarmOrderMethod, FarmProfileRecord, FarmReadiness, FarmReadinessBlocker, FarmSetupDraft,
+ FarmSetupProjection, FarmSummary, FarmerActivationProjection, FarmerSection,
+ FulfillmentWindowId, FulfillmentWindowRecord, LoggedOutStartupProjection, OrderId,
+ OrderStatus, OrdersFilter, PackDayBatchPrintArtifact, PackDayBatchPrintFailureKind,
+ PackDayBatchPrintStatus, PackDayExportInstanceId, PackDayExportStatus,
+ PackDayHostHandoffKind, PackDayHostHandoffStatus, PackDayPackListRow,
PackDayPrintFailureKind, PackDayPrintKind, PackDayPrintStatus, PackDayProductTotalRow,
PackDayProjection, PackDayRosterRow, PersonalSection, PickupLocationId,
- PickupLocationRecord, ProductEditorDraft, ProductStatus, ProductsFilter, ProductsSort,
- RecoveryKind, RecoveryRecordId, ReminderDeliveryState, ReminderFeedProjection,
- ReminderKind, SelectedSurfaceProjection, SettingsPreference, SettingsSection,
- ShellSection, TodayAgendaProjection, TodaySetupTask, TodaySetupTaskKind, TodaySummary,
+ PickupLocationRecord, ProductEditorDraft, ProductId, ProductStatus, ProductsFilter,
+ ProductsSort, RecoveryKind, RecoveryRecordId, ReminderDeliveryState,
+ ReminderFeedProjection, ReminderKind, SelectedAccountProjection, SelectedSurfaceProjection,
+ SettingsPreference, SettingsSection, ShellSection, TodayAgendaProjection, TodaySetupTask,
+ TodaySetupTaskKind, TodaySummary,
};
use radroots_app_remote_signer::{
RadrootsAppRemoteSignerPendingSession, RadrootsAppRemoteSignerSessionRecord,
@@ -5819,7 +5838,11 @@ mod tests {
farm_record.owner_account_id.as_deref(),
Some(account_id.as_str())
);
- assert!(farm_record.owner_pubkey.as_deref().is_some_and(is_hex_64));
+ let owner_pubkey = farm_record
+ .owner_pubkey
+ .as_deref()
+ .expect("farm owner pubkey");
+ assert!(is_hex_64(owner_pubkey));
assert!(
farm_record
.farm_id
@@ -5832,6 +5855,7 @@ mod tests {
.as_ref()
.expect("farm local work payload");
assert_eq!(farm_payload["scope"], "app");
+ assert_eq!(farm_payload["exportability"]["state"], "exportable");
assert_eq!(farm_payload["document"]["farm"]["name"], "Green Farm");
assert_eq!(
farm_payload["document"]["listing_defaults"]["delivery_method"],
@@ -5857,19 +5881,23 @@ mod tests {
listing_record.owner_account_id.as_deref(),
Some(account_id.as_str())
);
+ assert_eq!(listing_record.owner_pubkey.as_deref(), Some(owner_pubkey));
assert_eq!(listing_record.farm_id, farm_record.farm_id);
+ let expected_listing_addr_prefix = format!("30402:{owner_pubkey}:");
assert!(
listing_record
.listing_addr
.as_deref()
.expect("listing address")
- .starts_with("30402:")
+ .starts_with(expected_listing_addr_prefix.as_str())
);
let listing_payload = listing_record
.local_work_json
.as_ref()
.expect("listing local work payload");
+ assert_eq!(listing_payload["exportability"]["state"], "exportable");
assert_eq!(listing_payload["document"]["kind"], "listing_draft_v1");
+ assert_eq!(listing_payload["document"]["seller_actor"]["pubkey"], owner_pubkey);
assert_eq!(listing_payload["document"]["product"]["title"], "Eggs");
assert_eq!(
listing_payload["document"]["primary_bin"]["price_amount"],
@@ -5883,6 +5911,97 @@ mod tests {
}
#[test]
+ fn runtime_app_local_work_without_resolved_pubkey_is_non_exportable() {
+ let (runtime, paths) = bootstrapped_runtime("app_local_work_unresolved_pubkey");
+ let farm_id = FarmId::new();
+ let account = SelectedAccountProjection::new(
+ AccountSummary {
+ account_id: "acct_unresolved".to_owned(),
+ npub: "npub1unresolved".to_owned(),
+ label: Some("Unresolved".to_owned()),
+ custody: AccountCustody::RemoteSigner,
+ },
+ SelectedSurfaceProjection::new(ActiveSurface::Farmer),
+ FarmerActivationProjection::active(farm_id),
+ );
+ let saved_farm = FarmSummary {
+ farm_id,
+ display_name: "Green Farm".to_owned(),
+ readiness: FarmReadiness::Ready,
+ };
+ let farm_projection = FarmSetupProjection::from_saved_farm(saved_farm.clone());
+
+ {
+ let mut state = runtime.lock_state_mut();
+ let identity =
+ AppIdentityProjection::ready(vec![account.account.clone()], account.clone());
+ let _ = state
+ .state_store
+ .apply_in_memory(AppStateCommand::replace_identity_projection(identity));
+ state
+ .append_app_farm_local_work_record(&account, &farm_projection, &saved_farm)
+ .expect("unresolved farm local work should append");
+ state
+ .append_app_listing_local_work_record(
+ ProductId::new(),
+ &ProductEditorDraft {
+ title: "Eggs".to_owned(),
+ subtitle: "Fresh eggs".to_owned(),
+ unit_label: "dozen".to_owned(),
+ price_minor_units: Some(750),
+ price_currency: "USD".to_owned(),
+ stock_quantity: Some(12),
+ availability_window_id: None,
+ status: ProductStatus::Draft,
+ },
+ )
+ .expect("unresolved listing local work should append");
+ }
+
+ let records = shared_local_event_records(&paths);
+ let app_records = records
+ .iter()
+ .filter(|record| record.source_runtime == SourceRuntime::App)
+ .collect::<Vec<_>>();
+ assert_eq!(app_records.len(), 2);
+ assert!(
+ app_records
+ .iter()
+ .all(|record| record.owner_account_id.as_deref() == Some("acct_unresolved"))
+ );
+ assert!(app_records.iter().all(|record| record.owner_pubkey.is_none()));
+ assert!(
+ app_records
+ .iter()
+ .all(|record| record
+ .local_work_json
+ .as_ref()
+ .is_some_and(|payload| payload["exportability"]["state"] == "identity_unresolved"
+ && payload["exportability"]["reason"] == "canonical_hex_pubkey_required"))
+ );
+ let listing_record = app_records
+ .iter()
+ .find(|record| {
+ record
+ .local_work_json
+ .as_ref()
+ .and_then(|payload| payload["record_kind"].as_str())
+ == Some("listing_draft_v1")
+ })
+ .expect("listing local work record");
+ assert_eq!(listing_record.listing_addr, None);
+ assert!(
+ listing_record
+ .local_work_json
+ .as_ref()
+ .expect("listing payload")["document"]["seller_actor"]["pubkey"]
+ .is_null()
+ );
+
+ cleanup_bootstrapped_runtime_paths(&paths);
+ }
+
+ #[test]
fn runtime_manual_refresh_marks_failed_checkpoint_when_transport_is_unavailable() {
let runtime = memory_runtime();
let (account_id, _) = provision_ready_farmer_account(&runtime);