commit 79ff8c411788096c014b6478585207358274d917
parent f758400e8790560105b19f393d1752d1c1b32502
Author: triesap <tyson@radroots.org>
Date: Wed, 8 Jul 2026 08:47:16 +0000
sdk: record local import observations for signed workflows
- attach local import transport observations during signed workflow ingest
- keep SDK local provenance explicit with a stable local endpoint
- assert sync status includes local provenance plus publish observations
- add source-boundary coverage for the workflow ingest path
Diffstat:
3 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/crates/sdk/src/workflow_runtime.rs b/crates/sdk/src/workflow_runtime.rs
@@ -5,7 +5,9 @@ use crate::{
SdkIdempotencyKey, TargetPolicy, TargetSet, TransportProfile, runtime::sdk_now_ms,
};
use radroots_authority::{RadrootsActorContext, RadrootsEventSigner, sign_authorized_draft};
-use radroots_event_store::RadrootsEventIngest;
+use radroots_event_store::{
+ RadrootsEventIngest, RadrootsTransportObservation, RadrootsTransportObservationType,
+};
use radroots_events::{
RadrootsNostrEvent,
draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent},
@@ -15,10 +17,12 @@ use radroots_outbox::{
RadrootsOutboxDeliveryPlanInput, RadrootsOutboxEnqueueStatus,
RadrootsOutboxReticulumPreviewBehavior, RadrootsOutboxSignedOperationInput,
};
-use radroots_transport::RadrootsTransportSatisfactionPolicy;
+use radroots_transport::{RadrootsTransportKind, RadrootsTransportSatisfactionPolicy};
#[cfg(test)]
use sha2::{Digest, Sha256};
+const SDK_LOCAL_EVENT_ENDPOINT_URI: &str = "local:sdk";
+
pub(crate) struct SdkWorkflowEnqueueRequest<'a> {
pub(crate) operation_kind: &'static str,
pub(crate) actor: &'a RadrootsActorContext,
@@ -102,8 +106,15 @@ async fn enqueue_signed_workflow_event(
let partial_failure_digest_prefix =
digest_prefix(preflight.operation_idempotency_digest.as_str());
let event = event_from_signed(&signed_event);
+ let local_import_observation = RadrootsTransportObservation::new(
+ RadrootsTransportKind::Local,
+ SDK_LOCAL_EVENT_ENDPOINT_URI,
+ RadrootsTransportObservationType::LocalImport,
+ observed_at_ms,
+ )?;
let ingest = RadrootsEventIngest::new(event, observed_at_ms)
- .with_raw_json(signed_event.raw_json.clone());
+ .with_raw_json(signed_event.raw_json.clone())
+ .with_observation(local_import_observation);
let ingest_receipt = sdk._event_store.ingest_event(ingest).await?;
let outbox_input = signed_outbox_input(
request.operation_kind,
diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs
@@ -1491,6 +1491,24 @@ fn sdk_sync_status_sources_reject_retired_relay_shaped_generic_fields() {
}
#[test]
+fn sdk_workflow_runtime_records_local_import_observations() {
+ let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
+ let workflow_runtime = read_source(manifest_dir.join("src/workflow_runtime.rs").as_path());
+
+ for required in [
+ "SDK_LOCAL_EVENT_ENDPOINT_URI",
+ "RadrootsTransportKind::Local",
+ "RadrootsTransportObservationType::LocalImport",
+ ".with_observation(local_import_observation)",
+ ] {
+ assert!(
+ workflow_runtime.contains(required),
+ "SDK workflow runtime must retain local import observation witness `{required}`"
+ );
+ }
+}
+
+#[test]
fn sdk_transport_sources_keep_reticulum_preview_push_boundary() {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs
@@ -768,6 +768,7 @@ async fn sync_status_reports_pending_retryable_terminal_and_last_attempt_metadat
.expect("status");
assert_eq!(receipt.event_store.total_events, 3);
+ assert_eq!(receipt.event_store.transport_observations, 4);
assert_eq!(receipt.outbox.total_events, 3);
assert_eq!(receipt.outbox.pending_events, 1);
assert_eq!(receipt.outbox.retryable_events, 1);