Scope Scheduling: Capacity Slices and Preemption
Status: Specification
Version: 1.0
Date: 2026-02-11
Overview
ICN uses a scope hierarchy to manage resource allocation and priority. Scopes represent jurisdictional boundaries where capacity can be pledged and priority can be claimed.
Commons < Local < Cell < Org < Federation
This document specifies how scopes interact with scheduling, preemption, and resource allocation.
The Scope Hierarchy
Scope Definitions
| Scope | Description | Trust Model |
|---|---|---|
| Commons | Open participation, no membership required | Adversarial |
| Local | Your own node's resources | Strongest (self) |
| Cell | Small coordinated group | Coordinated |
| Org | Formal cooperative membership | Formal membership |
| Federation | Cross-cooperative treaties | Treaty-based |
Priority Order
Higher scopes have higher priority only when capacity is pledged:
Commons (lowest) < Local < Cell < Org < Federation (highest)
Key Invariants
- Commons is never authoritative - Commons can propose, never decide
- Commons is always preemptible - Any higher scope can evict commons work
- Priority requires pledged capacity - No pledge = no priority claim
- Node remains sovereign - Without pledges, only local matters
Capacity Slices
Definition
A capacity slice is a resource allocation pledged by a node to a higher scope:
pub struct CapacitySlice {
pub scope: Scope,
pub cpu_cores: f32, // e.g., 2.0 cores
pub memory_mb: u32, // e.g., 4096 MB
pub storage_gb: u32, // e.g., 100 GB
pub bandwidth_mbps: u32, // e.g., 100 Mbps
pub expires_at: Option<u64>,
}
Pledge Semantics
When a node pledges capacity to a scope:
- That capacity is reserved for the scope's work
- The scope's scheduler can claim the capacity
- Work at that scope level gets priority within the slice
- Lower scopes cannot use the pledged capacity
Example: Org Pledge
Node A pledges to Org "MyCoop":
- 2 CPU cores
- 4 GB RAM
- 50 GB storage
MyCoop's scheduler can now:
- Submit tasks that use up to 2 cores
- Expect 4 GB RAM available
- Store up to 50 GB of org data
Commons tasks CANNOT use this pledged capacity.
Preemption Rules
Preemption Within Pledged Capacity
Higher scopes can preempt lower scopes within their pledged slice:
Federation pledged work > Org pledged work > Cell pledged work > Local > Commons
Preemption Logic
fn should_preempt(running: &Task, incoming: &Task) -> bool {
// Higher scope always preempts within pledged capacity
if incoming.scope > running.scope {
if capacity_available_for(incoming.scope, incoming.resources) {
return true;
}
}
// Same scope: priority within scope
if incoming.scope == running.scope {
return incoming.priority > running.priority;
}
false
}
Preemption Examples
| Running Task | Incoming Task | Result |
|---|---|---|
| Commons indexing | Local backup | Preempt (local > commons) |
| Org service | Org service (higher prio) | Preempt (same scope, higher prio) |
| Cell compute | Org governance | Preempt if org has pledge |
| Federation settlement | Org service | No preempt (fed > org) |
| Local backup | Commons analytics | No preempt (local > commons) |
Graceful Preemption
Preempted tasks receive:
- Signal to checkpoint/pause
- Grace period (configurable, default 30s)
- State preservation if checkpointable
- Re-queue at original scope level
Scheduler Architecture
Per-Scope Queues
┌─────────────────────────────────────────┐
│ Federation Queue │ ← Highest priority
├─────────────────────────────────────────┤
│ Org Queue │
├─────────────────────────────────────────┤
│ Cell Queue │
├─────────────────────────────────────────┤
│ Local Queue │
├─────────────────────────────────────────┤
│ Commons Queue │ ← Lowest priority
└─────────────────────────────────────────┘
Scheduling Algorithm
fn schedule_next() -> Option<Task> {
// Try federation first (if pledged)
if let Some(task) = federation_queue.pop_if_capacity_available() {
return Some(task);
}
// Then org (if pledged)
if let Some(task) = org_queue.pop_if_capacity_available() {
return Some(task);
}
// Then cell (if pledged)
if let Some(task) = cell_queue.pop_if_capacity_available() {
return Some(task);
}
// Then local (always available)
if let Some(task) = local_queue.pop_if_capacity_available() {
return Some(task);
}
// Finally commons (uses remaining capacity)
commons_queue.pop_if_capacity_available()
}
Capacity Accounting
struct CapacityManager {
total: ResourceSet,
pledges: HashMap<Scope, CapacitySlice>,
in_use: HashMap<Scope, ResourceSet>,
}
impl CapacityManager {
fn available_for(&self, scope: Scope) -> ResourceSet {
match scope {
Scope::Commons => {
// Commons gets unpledged capacity only
self.total - self.pledged_total()
}
_ => {
// Higher scopes get their pledged slice
self.pledges.get(&scope).map(|s| s.resources).unwrap_or_default()
}
}
}
}
Pledge Management
Creating Pledges
pub struct PledgeRequest {
pub node_id: Did,
pub scope: Scope,
pub scope_id: String, // e.g., coop DID
pub capacity: CapacitySlice,
pub duration: Duration,
}
Pledge Lifecycle
- Propose: Node proposes pledge to scope
- Accept: Scope governance accepts pledge
- Activate: Capacity reserved, scheduler updated
- Renew: Pledge extended before expiry
- Expire/Revoke: Capacity returned to local
Pledge Verification
Scope schedulers verify pledges before claiming:
fn verify_pledge(node: &Did, scope: &Scope) -> Option<CapacitySlice> {
// Check pledge registry
let pledge = pledge_registry.get(node, scope)?;
// Verify not expired
if pledge.is_expired() {
return None;
}
// Verify scope membership
if !scope.is_member(node) {
return None;
}
Some(pledge.capacity)
}
Service Level Agreements
SLA Types
| Scope | SLA Model |
|---|---|
| Commons | Best-effort, no guarantees |
| Local | Node operator's choice |
| Cell | Informal agreement |
| Org | Formal SLA with penalties |
| Federation | Treaty-defined SLA |
SLA Metrics
pub struct ServiceLevelAgreement {
pub availability: f64, // e.g., 0.99 = 99%
pub latency_p99_ms: u32, // e.g., 100ms
pub throughput_rps: u32, // e.g., 1000 req/s
pub penalty_credits: u64, // Credits for SLA breach
}
SLA Enforcement
For Org and Federation scopes:
- Monitor: Track availability, latency, throughput
- Report: Periodic SLA compliance reports
- Compensate: Automatic credit transfer on breach
- Escalate: Governance proposal for chronic breach
Implementation Notes
Data Structures
// Scope enum
pub enum Scope {
Commons,
Local,
Cell { cell_id: String },
Org { org_did: Did },
Federation { fed_id: String },
}
impl Ord for Scope {
fn cmp(&self, other: &Self) -> Ordering {
self.priority_level().cmp(&other.priority_level())
}
}
impl Scope {
fn priority_level(&self) -> u8 {
match self {
Scope::Commons => 0,
Scope::Local => 1,
Scope::Cell { .. } => 2,
Scope::Org { .. } => 3,
Scope::Federation { .. } => 4,
}
}
}
Scheduler Hooks
The scheduler must integrate with:
- Gossip - Receive task submissions, broadcast results
- Trust graph - Verify scope membership
- Ledger - Record usage, settle capacity payments
- Governance - Process pledge proposals
Rationale
Why Scopes?
Without scopes:
- All work competes equally for resources
- No way to guarantee capacity for cooperatives
- Commons abuse could starve legitimate work
With scopes:
- Cooperatives can guarantee resources for members
- Commons remains open but preemptible
- Fair sharing without centralized rationing
Why Pledges?
Pledges create:
- Accountability: Node commits resources
- Predictability: Scope can plan capacity
- Incentive alignment: Pledge = stake in scope success
Why Preemption?
Preemption enables:
- Elastic commons: Use spare capacity
- Priority enforcement: Critical work first
- Graceful degradation: Shed load under pressure
References
docs/design/compute-classes.md- LC/UC specificationdocs/design/deterministic-core.md- ICN-DC specificationdocs/design/DETERMINISTIC_COMPUTE_SPRINT.md- Implementation plan