diff --git a/Cargo.lock b/Cargo.lock index 3c3e9fb5..a987af17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2941,6 +2941,7 @@ dependencies = [ "tempfile", "thiserror 2.0.18", "tokio", + "tracing", "uuid", ] diff --git a/crates/pod-registry/src/mutate.rs b/crates/pod-registry/src/mutate.rs index f186f5d7..1879a16a 100644 --- a/crates/pod-registry/src/mutate.rs +++ b/crates/pod-registry/src/mutate.rs @@ -39,6 +39,16 @@ pub fn register_pod( /// Register a top-level Pod with explicit deny rules that reduce the /// claimed effective write scope. +/// +/// Conflict semantics: if every Pod overlapping a requested allow rule +/// is fully covered by one of `scope_deny`, the conflict is suppressed +/// and the registration proceeds. The check is structural (deny ⊇ +/// competitor.rule), not relational — it does not verify that the +/// competitor actually descends from this Pod's prior delegations. +/// In practice this is safe because the canonical caller is `restore`, +/// which derives `scope_deny` from the session's own snapshot, so any +/// covered competitor is guaranteed to be a descendant of the original +/// allocation. Direct callers must uphold the same invariant. pub fn register_pod_with_deny( guard: &mut LockFileGuard, pod_name: String, diff --git a/crates/session-store/Cargo.toml b/crates/session-store/Cargo.toml index 3867f11d..da16aafb 100644 --- a/crates/session-store/Cargo.toml +++ b/crates/session-store/Cargo.toml @@ -16,6 +16,7 @@ thiserror = { workspace = true } sha2 = { workspace = true } hex = "0.4.3" protocol = { workspace = true } +tracing.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/session-store/src/session_log.rs b/crates/session-store/src/session_log.rs index 8c300f79..f5f2bd53 100644 --- a/crates/session-store/src/session_log.rs +++ b/crates/session-store/src/session_log.rs @@ -311,7 +311,15 @@ pub fn collect_state(entries: &[HashedEntry]) -> RestoredState { domain, payload, .. } => { if domain == POD_SCOPE_EXTENSION_DOMAIN { - state.pod_scope = serde_json::from_value(payload.clone()).ok(); + match serde_json::from_value::(payload.clone()) { + Ok(snapshot) => state.pod_scope = Some(snapshot), + Err(err) => { + tracing::warn!( + error = %err, + "discarding malformed pod.scope snapshot from session log" + ); + } + } } state.extensions.push((domain.clone(), payload.clone())); }