#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncClass {
Portable,
DeviceLocal,
}
impl SyncClass {
pub fn is_portable(self) -> bool {
matches!(self, SyncClass::Portable)
}
}
#[derive(Debug, Clone, Copy)]
pub struct SurfacePolicy {
pub domain: &'static str,
pub class: SyncClass,
pub note: &'static str,
}
use SyncClass::{DeviceLocal, Portable};
pub const SURFACE_POLICIES: &[SurfacePolicy] = &[
SurfacePolicy {
domain: "agent_permissions",
class: Portable,
note: "per-agent × per-tier posture (always-allow/require-approval/deny) — OS-agnostic intent; \
the OS-level grant it may reference is device-local and resolved per platform",
},
SurfacePolicy {
domain: "messaging_allowlist",
class: Portable,
note: "which handles/channels may approve — policy, not credentials",
},
SurfacePolicy {
domain: "agent_definitions",
class: Portable,
note: "declarative agents (agents.json / declagents) — portable definitions",
},
SurfacePolicy {
domain: "routing_priors",
class: Portable,
note: "learned capability-routing success priors — improves every device",
},
SurfacePolicy {
domain: "memory_graph",
class: Portable,
note: "the knowledge/identity/skill graph — the point of shared memory across devices",
},
SurfacePolicy {
domain: "approvals_ledger",
class: Portable,
note: "HITL decisions keyed by a stable fingerprint — a decision made on one device is \
honored on all (no re-prompting the same hazard per machine)",
},
SurfacePolicy {
domain: "parslee_tokens",
class: DeviceLocal,
note: "each device mints its own via login (correct); a token is a secret and per-device",
},
SurfacePolicy {
domain: "keychain_secrets",
class: DeviceLocal,
note: "Slack bot/app tokens et al. live in the OS keychain — a secret; the portable \
messaging config references them by a device-local keychain ref, re-provisioned per device",
},
SurfacePolicy {
domain: "permission_grants",
class: DeviceLocal,
note: "the OS's actual yes/no for a capability (macOS TCC, etc.) — re-obtained per device; \
syncing a grant across OSes is meaningless and unsafe",
},
SurfacePolicy {
domain: "machine_paths",
class: DeviceLocal,
note: "cwd/home/worktree/model-cache paths differ per machine and OS",
},
SurfacePolicy {
domain: "device_pairing",
class: DeviceLocal,
note: "the messaging pairing code binds one channel to this device",
},
SurfacePolicy {
domain: "voiceprints",
class: DeviceLocal,
note: "voice enrollment is bound to this device's microphone/enrollment audio",
},
];
pub fn policy_for(domain: &str) -> Option<&'static SurfacePolicy> {
SURFACE_POLICIES.iter().find(|p| p.domain == domain)
}
pub fn is_portable(domain: &str) -> bool {
policy_for(domain)
.map(|p| p.class.is_portable())
.unwrap_or(false)
}
pub fn portable_domains() -> impl Iterator<Item = &'static str> {
SURFACE_POLICIES
.iter()
.filter(|p| p.class.is_portable())
.map(|p| p.domain)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secrets_and_os_grants_are_never_portable() {
for d in [
"parslee_tokens",
"keychain_secrets",
"permission_grants",
"machine_paths",
"device_pairing",
"voiceprints",
] {
assert!(!is_portable(d), "{d} must stay device-local");
}
}
#[test]
fn authored_policy_is_portable() {
for d in [
"agent_permissions",
"messaging_allowlist",
"agent_definitions",
"routing_priors",
"memory_graph",
"approvals_ledger",
] {
assert!(is_portable(d), "{d} is authored policy — should sync");
}
}
#[test]
fn unknown_domains_fail_closed_to_device_local() {
assert!(!is_portable("some_new_unclassified_surface"));
assert!(policy_for("some_new_unclassified_surface").is_none());
}
#[test]
fn every_policy_has_a_rationale() {
for p in SURFACE_POLICIES {
assert!(!p.note.is_empty(), "{} needs a rationale", p.domain);
}
assert_eq!(portable_domains().count(), 6);
}
}