Deterministic Scoped Task Model v1
September 10, 2026 · View on GitHub
- Status: implemented proof model; v0.4.0 regression evidence is HOSTED GREEN. A separately specified bounded Rust scoped-thread runtime exists; language task syntax and backend task lowering remain outside this model.
- Version: 0.1
- Audience: language, compiler, runtime, and conformance-test implementers; agents auditing structured-concurrency semantics before any implementation exists
Summary
This document fixes the bounded target-neutral model that a future
structured-concurrency implementation must preserve. The repository contains
src/scoped_tasks.rs, a deterministic proof-data model of scoped task
execution: a bounded task DAG inside one strict scope tree, sequential
scheduling in canonical stable-id order, sticky cancellation propagation,
children-before-parents cleanup on scope exit in reverse completion order,
first-failure stickiness with sibling draining, and closed per-task
Sendable/Shareable annotations.
The proof module deliberately contains no threads, no async runtime, no scheduler
integration, no language syntax, no parser/HIR/Graph/backend changes, and no
Sendable checking of real programs. Like the callable-v3 settlement model,
everything it produces is evidence of what a conforming implementation MUST do,
never authority to execute anything. Real closure execution is owned by
Structured Tasks Runtime v1, including an
invocation-owned HTTPS task whose provider must settle before result
publication. A modeled task body here is still a closed scripted outcome
(Succeed, Fail(Semantic), or Fail(Physical(nonzero))); this proof module
runs no work and grants no network authority.
The key rule is:
Within one scope tree, tasks can never outlive their scope: scheduling, cancellation, and cleanup are fully determined by canonical stable-id order, cancellation marks descendants before any sibling starts new work, children finalize before parents in reverse completion order, and the first drained failure wins over everything that happens later — including later cancellation.
Relationship to existing contracts
This model extends, but does not replace, existing contracts:
- RFC 0003 defines target-neutral semantic cleanup order and sticky failure selection for single-function cleanup plans; this model lifts the same ordering discipline to a task DAG.
- RFC 0004 defines recovery settlement for native owned calls; its nonzero-physical-failure convention and its evidence-not-authority framing are reused here.
- Cleanup-plan vectors remain canonical execution order; this model never repairs, sorts, or substitutes them.
- The completion-matrix "Structured concurrency" row remains far from complete; the bounded host runtime and its HTTPS helper do not provide language task syntax, actors/reducers, synchronization, dependency scheduling, or verified task lowering on native and Wasm backends.
Normative goals
The terms MUST, MUST NOT, SHOULD, and MAY are normative.
For an admitted model:
- Every scope except the root MUST have exactly one parent and exactly one declared join from that parent; joining anything else MUST be rejected.
- Task dependencies MUST form an acyclic bounded graph whose prerequisite scopes lie on the dependent's own scope lineage; a cross-branch dependency MUST be rejected as an escaping reference.
- Scheduling MUST be sequential and deterministic: among ready tasks, the canonical smallest stable identity starts first.
- Cancelling a scope MUST mark every descendant scope and MUST materialize those cancellations — announcing each cancelled scope and cancelling each pending task — before starting any other work.
- An already-started task MUST drain to its scripted outcome even after cancellation; only pending tasks become cancelled.
- Scope exit MUST finalize started tasks in exact reverse completion order, then exit deepest-first so children always finalize and exit before parents.
- The first failure observed in a subtree MUST win stickily: independent siblings still drain to completion, dependents of failed prerequisites are abandoned as cancelled, and a drained failure reported at scope exit beats a late cancellation mark.
- Every structural ambiguity — duplicate identities, unknown endpoints, self or duplicate dependencies, cycles, missing/double/orphan joins, zero physical failure codes, and bound or work-budget violations — MUST fail closed at construction or at the offending operation.
Bounded structure
semaprax.scoped-tasks-model.v1 enforces:
| Quantity | Maximum |
|---|---|
| Scopes per model | 4,096 |
| Tasks per model | 4,096 |
| Dependency edges per model | 65,536 |
Validation work units (tasks * (edges + 1) + scopes) | 1,000,000 |
Scope identities form a strict containment tree with exactly one root. Task
identities are globally unique. Joins are not free-form edges: the constructor
requires exactly one waiter = parent(target) join per non-root scope, rejects
a join targeting the root or naming any non-parent waiter as an orphan join,
and rejects duplicates as double joins. These checks make "tasks cannot outlive
their scope" structural rather than advisory.
Closed state vocabulary
Task states are exactly Pending -> Started -> Completed | Failed, with
Cancelled reachable from Pending. A started task never becomes cancelled;
it drains to its declared outcome. Failure kinds are Semantic and
Physical(u32) where zero is rejected against the settlement convention that
zero means success. Per-task lifetime annotations are the closed pairs
Sendable/NotSendable and Shareable/NotShareable; they are recorded
projections of declared intent and imply no analysis of any real program.
Observable events are closed too: started, completed, failed,
cancelled, scope_cancelled, finalized, and scope_exited (carrying
Success, Failed { task, failure }, or Cancelled). Step precedence is:
drain the running task, announce newly cancelled scopes, cancel affected
pending tasks, start the smallest ready task, abandon permanently blocked
tasks, finalize in reverse completion order, then exit finished scopes
deepest-first with ties resolved to the canonical smaller identity.
Deterministic traces and digests
Every run projects a canonical JSON trace bound to the model fingerprint and the terminal root outcome:
{"schema":"semaprax.scoped-tasks-trace.v1","model_fingerprint":"…","events":[…],"root_outcome":…,"first_failure":…}
Model and trace projections use separately domain-separated SHA-256
fingerprints (semaprax.scoped-tasks-model-fingerprint.v1 and
semaprax.scoped-tasks-trace-fingerprint.v1) over length-prefixed bytes, so
identical logical models built in different input orders are byte-identical and
cross-domain digest confusion fails. These projections are test evidence, not a
wire format.
Required executable evidence
tests/cleanup_backends/scoped_tasks_model.rs plus seven focused module units currently
cover:
- pinned known-answer trace digests for four canonical scenarios:
- join-all:
c2c1ac40d3ce622bd1ac07984a88978dba13c79be2a568b9680943ccb07dbb91 - cancellation mid-scope:
98a5bf2f423a7a4d82f5edf2fb9f5374821e478b988871ef5a3635329c2d256b - failure drain with abandoned dependent:
b51cf73d42c73a97bc71a1e54b791896d053cdd5bcf4e61bfac6c2de545c6f6c - nested scopes with children-before-parents cleanup:
051da66037c3a17b8e58fda8f12902dcdc53f198e3c7ed1d464c65d239def03d
- join-all:
- exact event sequences for stable-id scheduling, reverse-completion finalization, cancellation-before-new-work, running-work draining under cancellation, sticky failure beating late cancellation, and dependent abandonment;
- hostile construction rejections: escaping sibling-scope dependency, double join, orphan join, duplicate/self/cyclic dependencies, duplicate identities, multiple or missing roots, scope cycles, zero physical failure code, and scope/task/bound/work-budget violations;
- hostile run operations: unknown-scope cancellation, post-completion
cancellation, effect-free repeated cancellation, premature
finish, and quiescent stepping after completion; - determinism under full input permutation (identical fingerprints, JSON, event vectors, and digests);
- byte-pinned canonical trace projection and JSON-validity parsing of both projections; and
- domain separation between model fingerprints and trace digests, including divergence between empty and complete traces.
These cases prove the bounded deterministic model only.
Explicit nonclaims
Deterministic Scoped Task Model v1 adds no language syntax, no parser, HIR,
Graph, verifier, formatter, CLI, compiler-backend, or Wasm change, and no
runtime: no threads, fibers, executors, scheduler integration, timers, I/O, or
real concurrent execution of any kind. It executes no user code — task bodies
are closed scripted outcomes. It performs no Sendable/Shareable checking
of real programs, no aliasing or data-rules analysis, no deadlock or liveness
proof beyond the modeled drain barrier, and no timing, preemption, fairness, or
parallelism claim: the schedule is sequential by construction. It grants no
authority, mutates no source, claims no actors/reducers or synchronization
primitives, and satisfies none of the remaining completion-gate requirements
for the Structured concurrency row. No feature is "implemented" beyond this
proof data and its executable evidence.
Current status
The model, its module units, and the integration suite are locally evidenced under the standard quality gates. There is no public surface change, no diagnostic family, and no backend behavior change. Any future runtime or language integration must preserve the scheduling, cancellation, cleanup, stickiness, and serialization rules above and must not cite this document as evidence for execution semantics it does not implement.