feat(shared): SdkCallCx, Services bundle, ServiceEventEmitter trait shape
Foundation for the v1.1.x stateful SDK services. Lands the shape only:
- SdkCallCx — per-call context plumbed into every future service
trait method (app_id, principal, execution/request ids, trigger
depth slots).
- Services — empty non_exhaustive bundle; v1.1.1 (KV) adds the first
field, subsequent PRs follow.
- ServiceEventEmitter — async trait future services emit through;
real outbox-backed impl lands with triggers in v1.1.1. NoopEventEmitter
is the v1.1.0 default.
No behaviour change. Subsequent commits in this PR plumb these types
through executor-core and the orchestrator dispatch path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
54
crates/shared/src/sdk_cx.rs
Normal file
54
crates/shared/src/sdk_cx.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
//! `SdkCallCx` — per-call context every stateful SDK service receives.
|
||||
//!
|
||||
//! Service trait methods (added by subsequent v1.1.x PRs starting with
|
||||
//! KV) all take `&SdkCallCx` so they can:
|
||||
//! * scope by `app_id` for cross-app isolation,
|
||||
//! * audit `principal` when authenticated,
|
||||
//! * carry `execution_id` / `request_id` into emitted events,
|
||||
//! * bound trigger chains via `trigger_depth` / `root_execution_id`.
|
||||
//!
|
||||
//! The struct lives in `picloud-shared` (not `executor-core`) because
|
||||
//! future service impls live in `manager-core` and the trait that hands
|
||||
//! the cx in is shared by both sides. Pure value type — no handles, no
|
||||
//! DB pool references, no allocations beyond what's in `Principal`.
|
||||
|
||||
use crate::{AppId, ExecutionId, Principal, RequestId};
|
||||
|
||||
/// Per-invocation context for every stateful SDK service call.
|
||||
///
|
||||
/// Constructed once at the start of an invocation by `executor-core`
|
||||
/// from the incoming `ExecRequest`, then handed (by reference) to every
|
||||
/// service trait method the script triggers during execution. Services
|
||||
/// MUST derive `app_id` from this struct — never from script-passed
|
||||
/// arguments — to preserve cross-app isolation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SdkCallCx {
|
||||
/// Owning application for this invocation. Source of truth for
|
||||
/// every `(app_id, …)` storage lookup the script makes.
|
||||
pub app_id: AppId,
|
||||
|
||||
/// Caller identity, when authenticated. `None` for unauthenticated
|
||||
/// data-plane HTTP requests (the common case for public endpoints);
|
||||
/// `Some` when the call came in via the dashboard, an API key, or a
|
||||
/// future authed surface.
|
||||
pub principal: Option<Principal>,
|
||||
|
||||
/// Unique id for THIS execution. Matches `ExecRequest.execution_id`.
|
||||
pub execution_id: ExecutionId,
|
||||
|
||||
/// Unique id for the ingress request that started the chain. The
|
||||
/// same `request_id` is shared across every execution triggered by
|
||||
/// the same request (direct + trigger fan-out).
|
||||
pub request_id: RequestId,
|
||||
|
||||
/// `0` for direct invocations (HTTP request, manual run). Each
|
||||
/// indirect invocation through the triggers framework (v1.1.1)
|
||||
/// increments this; the dispatcher rejects beyond a configured
|
||||
/// ceiling to prevent runaway feedback loops.
|
||||
pub trigger_depth: u32,
|
||||
|
||||
/// `== execution_id` when `trigger_depth == 0`; otherwise the
|
||||
/// `execution_id` of the original ingress execution. Lets the audit
|
||||
/// log group every fan-out execution under the originating event.
|
||||
pub root_execution_id: ExecutionId,
|
||||
}
|
||||
Reference in New Issue
Block a user