Distributed task-tree values: W3C trace-compatible task identities under an application-supplied URN scheme, validated lifecycle transitions, parent-child lineage, progress, and timestamps.
This crate is deliberately runtime-independent — no async, no I/O, no executor. It is the value layer a distributed task engine is built on; the engine (registries, admission, execution, event streams) is yours.
- Identity — a
TaskTreeIdis W3C trace-sized (16 bytes) and aTaskNodeIdspan-sized (8 bytes), so every task maps directly onto Trace Context: a tree id parses from its URN, a bare trace id, or a fulltraceparentheader. Every tree has one reserved root node, andTaskId::childmints siblings inside the tree. - Your namespace, not ours — identities are minted under a
UrnSchemethe application defines; this crate ships no namespace of its own, so no third party can mint another system's URNs and two systems' identities can never be confused, even at the type level. Prefixes are validated at compile time. - Lifecycle —
TaskStatuswith a validated transition machine (admitted → queued → running → sealing → done/failed/cancelled); terminal states admit no exits, and the irreversible sealing region refuses cancellation. Illegal edges are unrepresentable asTaskTransitionvalues. - Terminal cause — every move into
failedorcancelledcarries aTerminalCause: the originating event (caller cancel, session close, deadline, exhausted budget, executor failure, stall, lost node, host eviction, host restart), whether this task was its subject or collateral of the cascade, the terminal status that event ends in, and the party (FaultSide) it is attributed to. A cause-less terminal edge, a cause on a non-terminal edge, or a cause that ends in the other terminal status is refused at construction and at deserialization. - Lineage —
TaskLineageproves a parent-child edge is within one tree and never self-referential, enforced again on deserialization. - Progress — typed units, bounded aggregation, display fractions, and lifecycle timestamps.
- Resource envelopes — an abstract
Tokensunit with checked arithmetic; aTokenBudgetthat attenuates to children running in the same process and reserves shares for delegated ones (siblings split the pool, nothing is handed out twice);ResourceBudget,ResourceReservation, andResourceActualthat compose bottom-up per resource (sequential children sum wall time, parallel children take the maximum; tokens and wall work always sum); and a typedBudgetRefusal. No prices, factors, or ledgers: what a token buys is the application's business.
use tasktree::{TaskId, TaskLineage, TaskStatus, UrnScheme};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct MyUrn;
impl UrnScheme for MyUrn {
const TASK_PREFIX: &'static str = "urn:my-app:task:";
const TREE_PREFIX: &'static str = "urn:my-app:task-tree:";
}
let root: TaskId<MyUrn> = TaskId::generate();
let child = root.child();
assert!(root.as_str().starts_with("urn:my-app:task:"));
let edge = TaskLineage::try_new(child, root).expect("same tree, not self");
assert!(TaskStatus::Running.validate_transition(TaskStatus::Sealing).is_ok());
# let _ = edge;Serde uses the URN string form throughout, and every deserialization re-validates: a foreign prefix, a zero identity, or an illegal transition never becomes a value.
Licensed under either of:
- Apache License, Version 2.0 (
LICENSE-APACHE); - MIT License (
LICENSE-MIT).