Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Telltale is a session-typed execution system for building protocol-critical concurrent and distributed programs. It is designed as a conservation system over protocol semantics: all semantically meaningful behavior is expressed in terms of six conserved quantities, and all other behavior is erased or reduced to those quantities. See Conservation Framework for the full design philosophy.

The system enables writing distributed protocols from a global perspective with automatic projection to local implementations. The Lean 4 formalization provides mechanized proofs of preservation, progress, coherence, and harmony. The same choreography compiles to native and WASM targets.

Content addressing assigns cryptographic identities to protocol artifacts. Asynchronous subtyping uses SISO decomposition with orphan-free deadlock checks. Endpoint transfer semantics support ownership handoff at runtime with progress token migration.

Core Concepts

Session types encode communication protocols as types. A type like !String.?Int.end sends a string, receives an integer, then closes the channel. The compiler checks that implementations follow the protocol contract.

Multiparty session types extend this to protocols with three or more participants. Global types describe the full protocol. Projection transforms global types into local types for each participant.

Choreographic programming builds on global types. A choreography describes computations and message flow from a neutral perspective. Endpoint projection generates the local implementation for each role.

Design Philosophy

Telltale enforces conservation over six properties: evidence, authority, identity, commitment, structure, and premise. These properties are mutually constitutive. A coherent protocol state is a simultaneous assignment of all six. See Conservation Framework for the conservation laws, erasure and reduction principles, and the closed semantic core object set.

Within that conservation framework, protocol-critical capability semantics are first class. The runtime and Lean model distinguish four capability classes: admission, ownership, evidence, and transition. This taxonomy covers protocol-critical authority, finalization, and handoff/reconfiguration semantics. It does not attempt to absorb general host application authorization.

See Capability Model for the full taxonomy.

Runtime Architecture

The execution architecture has three layers. The protocol machine is a deterministic small-step reducer that commits all protocol-visible truth. The guest runtime wraps the protocol machine with typed effect interfaces and concrete handlers. The host runtime is the surrounding application that provides time, storage, network, and process lifecycle.

Typed effect interfaces are the operational vocabulary between the protocol machine and the world. Internal handlers realize scheduling, dispatch, and replay. External handlers realize storage, network, and domain-specific integrations. See Architecture and Protocol Machine Architecture for details.

The protocol machine also derives a first-class finalization subsystem from its semantic objects. Observed reads, authoritative reads, publications, materialization proofs, canonical handles, receipts, and semantic handoffs are not just helpers. They are explicit replay-visible objects in the runtime and the Lean model.

Effect Handlers

Communication operations are algebraic effects. Sending and receiving messages are abstract operations that handlers implement concretely. Programs specify what to communicate.

Handlers determine how messages are delivered. In-memory channels serve testing scenarios. TCP connections serve deployment scenarios. The protocol logic remains unchanged across execution strategies.

Protocol Machine

The protocol machine compiles local types to bytecode instructions. It manages scheduling, message buffers, and session lifecycle. The concurrency parameter N controls how many coroutines advance per round.

Lean Verification

The Lean 4 formalization spans roughly 653 files and 133k lines in the core libraries (generated metrics in lean/CODE_MAP.md). It covers global types, local types, projection, and operational semantics. Deadlock-freedom claims are assumption-scoped with explicit premises for well-typedness, progress reachability, and fair scheduling.

The telltale-bridge crate provides JSON export and import for cross-validation between Rust and Lean. See Lean Verification for the verification pipeline.

Document Index

DocumentTypeStatus
Getting StartedGuideInformative
ArchitectureConceptMixed
Code OrganizationReferenceInformative
TheoryConceptInformative
Choreographic DSLGuideMixed
Choreographic Projection PatternsReferenceMixed
DSL ExtensionsGuideMixed
Choreography Effect HandlersGuideMixed
Using Telltale HandlersGuideInformative
Effect Handlers and Session TypesReferenceNormative
Protocol Machine ArchitectureReferenceNormative
Protocol-Machine Bytecode InstructionsReferenceNormative
Session LifecycleReferenceNormative
Simulation OverviewGuideMixed
Simulation RunnerReferenceNormative
Simulation ScenariosGuideMixed
Simulation FieldsReferenceInformative
Simulation ViewerReferenceMixed
Simulation Viewer WebappGuideMixed
Session Graph SearchReferenceMixed
Rust-Lean Bridge and ParityReferenceNormative
Content AddressingReferenceMixed
Resource HeapReferenceMixed
TopologyGuideMixed
Lean VerificationReferenceMixed
Capability AdmissionReferenceNormative
Theorem ProgramConceptMixed
Theorem Pack InputsReferenceMixed
ExamplesGuideInformative
WASM GuideGuideInformative
API ReferenceReferenceInformative
Glossary and Notation IndexReferenceInformative
Verification InventoryReferenceInformative
Capability ModelReferenceNormative
Authority Language SurfaceReferenceNormative
Conservation FrameworkConceptNormative

Paths by Role

Library Users

Start with Getting Started. Then read Choreographic DSL. Continue with Examples and API Reference.

Protocol-Machine Integrators

Start with Architecture. Then read Effect Handlers and Session Types and Protocol Machine Architecture. Continue with Protocol-Machine Bytecode Instructions and Session Lifecycle. See Simulation Overview for testing.

Paper Reviewers

Start with Conservation Framework and Architecture. Then read Theory, Lean Verification, and Rust-Lean Bridge and Parity. Continue with Capability Admission and Theorem Program. See Theorem Pack Inputs and Glossary and Notation Index for reference.

Further Reading

For MPST theory, see A Very Gentle Introduction to Multiparty Session Types. For asynchronous subtyping, see Precise Subtyping for Asynchronous Multiparty Sessions.

For choreographic programming, see Introduction to Choreographies. For integration with session types, see Applied Choreographies.

See Glossary and Notation Index for shared terminology.

Conservation Framework

Telltale is a conservation system over protocol semantics. All semantically meaningful behavior is expressed in terms of conserved quantities. All other behavior is erased or reduced to those quantities.

This framework defines the six properties that Telltale conserves, the principles that constrain the programming model, and the semantic objects through which conservation is realized at runtime.

Conserved Properties

Telltale enforces conservation over six properties. Each property has a conservation law stating what the system guarantees across all protocol evolution.

Evidence

Evidence is the integrity of what has been established. Witnesses, proofs, and attestations are the concrete forms of evidence. Evidence backs protocol claims and gates protocol progression.

Conservation law: the evidential backing of any protocol claim cannot increase except through explicit witness production. It cannot decrease except through explicit invalidation. Evidence cannot be fabricated from weaker evidence.

Authority

Authority is the exclusivity of who may act. Ownership is the concrete form of authority. Authority determines which actor may produce evidence, commit state, or transfer control.

Conservation law: exactly one semantic owner holds authority over a given resource at any time. Authority cannot be duplicated. Transfer is explicit and invalidates prior authority.

Identity

Identity is the definiteness of object references. Identity distinguishes one unit of semantic work from another across retries, effects, and handoffs. Definite references are what make ownership meaningful and commitments trackable.

Conservation law: identity is stable across all transformations of an operation. It cannot be reconstructed from ambient context. It is assigned once and carried, never re-derived.

Commitment

Commitment is the account of outstanding obligations. Outstanding effects are the concrete form of commitment. A commitment is a promise that the system will produce a resolution for a given operation.

Conservation law: every commitment must resolve to exactly one of success, failure, timeout, cancellation, or invalidation. Commitments cannot silently disappear. The total number of unresolved commitments is always known.

Structure

Structure is the essential shape of the protocol. Multiparty session types are the concrete form of structure. Structure defines the topology of interaction, the composition of roles, and the coherence between local and global behavior.

Conservation law: the compositional structure of the protocol is defined entirely by its type. Local behavior must remain a valid projection of the global protocol. No runtime behavior can alter the protocol shape. See Theory for the session type foundations.

Premise

Premise is the explicitness of environmental assumptions. Failure models, fairness assumptions, and progress expectations are the concrete forms of premise. Premise determines what the protocol can rely on from its environment.

Conservation law: the system operating assumptions are explicit and fixed for a given execution context. Premise violation triggers explicit escalation rather than silent degradation. Escalation classes include blocked, degraded, and failed.

Property Relations

The six properties are mutually constitutive. They do not layer into a dependency hierarchy. A coherent protocol state is a simultaneous assignment of all six. A violation in any one can manifest as a failure in any other.

Erasure Principle

Any behavior that is not part of the conserved quantities is not part of the programming model. Scheduling, task execution, callback timing, retry loops, thread interleaving, and background task structure are implementation details. They must reduce to the conserved system.

Developers reason about evidence, authority, identity, commitment, structure, and premise. They extend the system through typed effect interfaces that enforce the conservation laws at the boundary. Domain logic plugs into contracts, not into concurrency.

Reduction Principle

All runtime behavior must reduce to the conservation framework.

Runtime phenomenonReduced form
async executioncommitment lifecycle
race conditionauthority violation
retry logicidentity + commitment
stale callbackinvalidated commitment
state buginvalid evidence or identity
timeoutpremise escalation
background taskdetached or structured commitment

This table maps common runtime phenomena to their conservation-framework equivalents. If a runtime behavior cannot be expressed in these terms, it does not belong in the model.

Eliminated Bug Classes

The system eliminates these classes of bugs by construction. Each maps to a violation of specific conserved properties.

Bug classDescriptionViolated propertyEliminated by
Hidden concurrencyWork outside structured controlcommitmentExplicit commitments, no ambient async
Authority ambiguityMultiple actors advancing same stateauthoritySingle semantic owner, explicit handoff
Weakening of evidenceFallback from strong to weak backingevidenceMonotonic witness strengthening, canonical handles
Silent failureWork disappearing or failing invisiblycommitmentCommitment conservation, typed outcomes
Late result racesStale results applied after handoffauthority, identityOutstandingEffect tracking, identity + owner validation
Observational state becoming evidenceCache or UI state treated as authoritativeevidenceSeparation of AuthoritativeRead and ObservedRead
Best-effort work defining successOptional work affecting terminal statecommitmentWork classification (required vs best-effort)
Unbounded waitingOperations that never resolvecommitment, premiseProgressContract, required escalation
Hidden reentrancySame domain re-entered unexpectedlystructureExplicit reentrancy rules
Multiple evidence sourcesMultiple publication pathsevidence, authorityCanonical publication path, proof-bound publication

Semantic Core Objects

The conservation framework is realized through a closed set of semantic objects. All protocol-visible language features must lower to these objects or to pure structural session terms. If a feature cannot be reduced to this semantic core, it is not admitted as part of the model.

ObjectRoleCapability class
RegionLocality and framing domain for structured coordinationstructural context
OperationInstanceStable identity for one semantic operation across retries, handoff, and replayidentity carrier
OutstandingEffectOne unresolved capability invocation with explicit lifecycle statecommitment/evidence support
SemanticHandoffExplicit transfer of responsibility and authority between actorstransition
AuthoritativeReadProof-carrying read surface that may support semantic commitmentevidence
ObservedReadNon-authoritative read surface for rendering, diagnostics, or advisory logicevidence
MaterializationProofTyped proof that a canonical artifact has been materially establishedevidence
CanonicalHandleNon-forgeable reference obtained from authoritative materializationevidence
ProgressContractExplicit liveness and budget contract governing waits and escalationpremise/commitment support

These objects are first-class in the Lean formalization, represented canonically in Rust, and referenced in the protocol machine semantic audit surface. See Protocol Machine Architecture for the runtime accessors and Lean Verification for the proof surfaces.

Implementation Techniques

Each implementation technique serves one or more conserved properties.

TechniqueDescriptionProperties conserved
Protocol machineSingle component that commits stateauthority, structure
Operation instancesExplicit identity for all semantic workidentity
Outstanding effectsAll deferred work tracked to resolutioncommitment
Semantic handoffExplicit authority transfer between actorsauthority, identity
Witness systemAll evidence is proof-bearingevidence
Capability algebraAuthority is explicit and constrainedauthority
Effect systemBoundary interactions are typed and classifiedcommitment, evidence
Progress contractsLiveness is explicit and enforcedcommitment, premise
Structured session decompositionAll concurrency follows session structurestructure, commitment
EnforcementType-level where possible, runtime fail-closed otherwiseall

Debugging Model

Every failure is a conservation failure. Every failure must be explainable as a violation of one or more conserved properties.

Instead of “the system hung,” a developer sees: which operation, which owner, which commitment, which required witness, which premise, and which escalation state. Every failure answers whether the protocol was correct, whether authority was modeled correctly, whether the premise was correct, whether the effect classification was correct, and whether the progress contract was correct.

Theory

This document explains the theory that Telltale builds on. It is organized by audience relevance. Developer-facing protocol concepts come first. Proof-oriented topics come last.

Foundations

Session types describe communication protocols as types. They let the type system check message order, branch structure, and termination behavior. Telltale uses them as the structure layer of its protocol model.

Why Session Types

Distributed programs fail when participants disagree about message order or branch choice. Ordinary data types do not describe those communication obligations. Session types add that missing protocol structure.

With session types, communication mistakes become type errors rather than runtime surprises. This is the main reason they matter in practice. They let developers state protocol shape directly and check it mechanically.

Binary Session Types

Binary session types describe one conversation between two parties. The core actions are send, receive, choice, and termination.

SyntaxMeaning
!T.Ssend a value of type T, then continue as S
?T.Sreceive a value of type T, then continue as S
⊕{...}choose one labeled branch
&{...}accept one labeled branch
endterminate the session

Binary types also rely on duality. If one side sends, the other side must receive. If one side chooses, the other side must branch on that choice.

Client: !Request.?Response.end
Server: ?Request.!Response.end

This pair is dual. The client sends then receives. The server receives then sends.

Multiparty Session Types

Multiparty session types extend the same idea to three or more roles. They separate the protocol into a global view and one local view per participant. This is the core abstraction behind Telltale choreographies.

Global types describe the whole protocol from a neutral perspective. Local types describe only what one role must send and receive. Projection computes those local types from the global type.

Global:
  Alice -> Bob: Request.
  Bob -> Carol: Forward.
  Carol -> Bob: Result.
  Bob -> Alice: Response.
  end

This global type describes the whole conversation. Projection gives Alice a two-step local view, Bob a four-step local view, and Carol a two-step local view.

Projection and Merging

Projection extracts one role-local type from the global type. When a message does not involve the target role, projection skips it. When a message does involve the target role, projection turns it into a local send or receive action.

Branching requires one extra operation called merge. If a participant is not the branch chooser, its projected local view must combine the possible continuations. Merge succeeds only when those continuations remain compatible from that participant’s viewpoint.

Protocol Constraints

Not every syntactically valid protocol is meaningful. Telltale relies on a set of constraints that a protocol must satisfy before projection and execution are well behaved. These are often called well-formedness conditions in the literature.

For global protocols, the main rules are:

  • recursion variables must be bound
  • communication branches must be non-empty
  • a role must not send to itself
  • recursion must pass through communication

For local protocols, the main rules are:

  • the type must be closed
  • recursion must unfold to observable communication or termination

These constraints are developer-facing. They are the rules a protocol must satisfy in order to project cleanly and support the later guarantees.

Recursion

Session types support recursive protocols through fixed points. This is how a protocol expresses repetition or long-running interaction.

μX. Alice -> Bob: Ping. Bob -> Alice: Pong. X

This protocol repeats the same request-response cycle forever. Recursion is valid only when unfolding eventually reaches observable communication.

Subtyping

Subtyping determines when one protocol can replace another. This matters when a component offers a more specific behavior than the caller requires.

In synchronous settings, subtyping follows the usual session-typing variance rules. In asynchronous settings, buffering allows some outputs to move earlier relative to inputs. Telltale implements asynchronous subtyping using the Bravetti et al. POPL 2021 algorithm.

Protocol Guarantees

This section describes the guarantees developers usually care about once a protocol satisfies the foundational constraints. These guarantees are still premise-scoped. They depend on explicit assumptions about execution and delivery.

Coherence

Coherence is the central MPST invariant. It states that the collective local state of the protocol still matches the global protocol state. Without coherence, projection would not give a trustworthy local view.

In Telltale, coherence is tracked per communication edge. Each edge relates the sender state, the receiver state, the relevant buffer contents, and the remaining global structure. This is the invariant that lets local endpoint behavior stay aligned with the choreography.

Preservation

Preservation states that a well-typed and coherent protocol state remains well-typed and coherent after a valid step. A correct protocol execution does not silently fall out of the typing discipline. This is the usual subject-reduction property.

Preservation is mainly a meta-level guarantee. Developers benefit from it because protocol execution stays inside the checked state space once it starts there.

Progress

Progress states that a well-typed protocol can continue unless it has already terminated. A participant does not get stuck waiting for behavior the protocol itself forbids.

This guarantee is premise-scoped. Telltale states those premises explicitly. They include well-typedness, reachability of communication, and fair scheduling.

Deadlock Freedom

Deadlock freedom is the developer-facing consequence of preservation plus progress under the declared premises. A well-typed protocol cannot reach a state where all participants are permanently blocked by protocol structure alone.

This guarantee does not ignore the environment. If the delivery model or scheduler violates the stated premises, the theorem does not apply. That separation is part of Telltale’s premise discipline.

Harmony

Harmony is a compilation-correctness property rather than a direct developer-facing liveness property. It states that global protocol evolution and projected local evolution stay in correspondence. This is what makes endpoint projection trustworthy.

project(step(G)) = localStep(project(G))

This equation says that stepping the choreography and then projecting gives the same result as projecting first and stepping locally. Developers benefit indirectly because generated endpoint behavior stays aligned with the choreography they wrote.

Operational Extensions

This section covers theory that matters once the protocol interacts with realistic runtime concerns. These topics are still user-relevant, but they are more specialized than the core session-typing model.

Delegation and Reconfiguration

Delegation transfers a session endpoint from one participant to another. This allows the active participant set to change during execution. It is the main extension beyond fixed-role MPST.

Delegation is safe only when the transferred endpoint state matches what the receiver can legally assume. Telltale captures those conditions with DelegationWF. The important developer-facing point is that endpoint transfer is governed by explicit protocol-side conditions, not by ambient host authority.

Delivery Models

Asynchronous session reasoning depends on the delivery model. Different delivery models allow different message orderings and therefore support different theorems.

Telltale parameterizes theorems over DeliveryModel. Important examples are FIFO, causal, and lossy delivery. This keeps the theorem shape stable while making delivery assumptions explicit.

Host-Runtime Ownership Contract

The Rust runtime adds an ownership contract around delegation, reconfiguration, and host-visible mutation. This layer uses concepts such as current owner capability, ownership generation, transfer receipts, rollback, and stale-owner rejection. It is an implementation hardening layer.

This runtime contract is aligned with theory-side predicates such as DelegationWF. It should not be read as a separate theorem family. It is the host/runtime realization of the protocol boundaries that the theory already identifies.

Verification Topics

This section is mainly for readers of the Lean formalization. These concepts matter for proofs and mechanization. They are not the first concepts a protocol author usually needs.

Bisimulation

Bisimulation compares two types by observable behavior rather than by literal syntax. It is useful when recursive types differ syntactically but unfold to the same behavior. This is a proof and equivalence technique.

In practice, bisimulation matters most when reasoning about proof identities, recursive encodings, and normalization arguments. It is less central for day-to-day protocol authoring.

De Bruijn Indices

The Lean formalization uses de Bruijn indices in foundational proof layers. This avoids name-capture problems during substitution and recursion reasoning. Rust-facing protocol APIs keep named recursion variables for usability.

If you are reading the proof code, expect de Bruijn forms in the foundational libraries. If you are writing protocols, this detail is mostly invisible.

Lean Formalization

The Lean formalization proves the core metatheory behind projection and protocol execution. Important theorem families include coherence, preservation, progress, and harmony. Current library structure and metrics are tracked in Lean Verification Code Map.

At a high level, SessionTypes and SessionCoTypes cover syntax and equivalence. Choreography covers projection and harmony. Protocol covers coherence and typing. Runtime covers the protocol-machine model and runtime correctness surfaces.

Theory in Telltale

Telltale implements the theory across Rust and Lean. The Rust crates provide executable data types and algorithms. The Lean libraries provide mechanized proofs and proof-facing models.

On the Rust side, telltale-types defines GlobalType and LocalTypeR. telltale-theory implements projection, merge, duality, and subtyping. The runtime and machine layers then realize those structures operationally.

On the Lean side, the formalization provides the theorem libraries that justify those structures. See Lean Verification for the proof pipeline and source layout. See Introduction for the system-level overview.

Conservation and Session Structure

Session types are Telltale’s concrete realization of structure conservation. The protocol type fixes the allowed interaction shape. Local behavior must remain a valid projection of that shape.

The other conserved properties extend the guarantee surface beyond session structure alone. Evidence governs witnesses and attestations. Authority governs exclusive ownership. Identity governs stable references. Commitment governs outstanding effects. Premise governs environmental assumptions.

In the runtime and Lean model, these properties are carried by explicit typed objects. They are not left as ambient host assumptions. See Conservation Framework for the full conservation model.

Further Reading

For deeper study of session type theory:

Within this documentation:

Architecture

Overview

Telltale compiles global protocol specifications into local session types for each participant. The system is organized as a conservation system over protocol semantics. All protocol-critical behavior reduces to six conserved properties: evidence, authority, identity, commitment, structure, and premise. See Conservation Framework for the full design philosophy.

The architecture has three compile-time stages and two runtime paths:

  1. DSL and parsing (choreographic syntax to AST)
  2. Projection (global protocol to local types)
  3. Code generation (local types to Rust code and effect programs)
  4. Effect handler execution (async interpreter with pluggable transports)
  5. Protocol-machine execution and simulation (protocol machine with scheduler and deterministic middleware)

The current formal-verification claim is narrower than this full architecture diagram. The Rust parser, lowering, projection, code generation, and macro entry paths are operationally checked and Lean-cross-validated, but they remain outside the current formal claim until a mechanized compiler-correctness proof exists for the claimed DSL subset.

Runtime Layering

The execution architecture separates three concerns. The protocol machine is a deterministic small-step reducer that is the sole authority over semantic progression. The guest runtime wraps the protocol machine with typed effect interfaces and concrete handlers. The host runtime is the surrounding system that provides time, storage, network, and process lifecycle.

Typed effect interfaces form the operational vocabulary between the protocol machine and the world. Internal handlers realize scheduling, dispatch, replay, and simulation. External handlers realize storage, network, and domain-specific host integrations. Both handler domains interpret the same typed interfaces.

Component Diagram

graph TB
    subgraph Input["Developer Input (Compile-Time)"]
        DSL["Choreography DSL<br/>Global Protocol Specification"]
    end

    subgraph Layer1["Layer 1: Parsing & AST Construction"]
        Parser["Parser<br/>(Pest Grammar)"]
        AST["AST<br/>Choreography + Protocol Tree"]
    end

    subgraph Layer2["Layer 2: Projection"]
        Proj["Projection Algorithm"]
        LT["Local Types<br/>(Per Role)"]
    end

    subgraph Layer3["Layer 3: Code Generation"]
        CodeGen["Code Generator"]
        Session["Generated Session Types"]
        Effects["Generated Effect Programs"]
    end

    subgraph Layer4["Layer 4: Effect Runtime"]
        Handler["Effect Handler<br/>(InMemory / Telltale)"]
        Transport["Transport Layer<br/>(Channels / Network)"]
        Exec["Running Protocol"]
    end

    subgraph Layer5["Layer 5: Protocol Machine + Simulation"]
        VMCompiler["Protocol-Machine Compiler<br/>(LocalTypeR → Bytecode)"]
        PM["Protocol Machine"]
        Scheduler["Scheduler<br/>(Policy-Based)"]
        Sessions["Session Store"]
        Buffers["Bounded Buffers"]
        Middleware["Simulator Middleware<br/>(Latency / Faults / Properties / Checkpoints)"]
    end

    DSL --> Parser
    Parser --> AST
    AST --> Proj
    Proj --> LT
    LT --> CodeGen
    CodeGen --> Session
    CodeGen --> Effects
    Session --> Handler
    Effects --> Handler
    Handler --> Transport
    Transport --> Exec
    LT --> VMCompiler
    VMCompiler --> PM
    PM --> Scheduler
    Scheduler --> Sessions
    Sessions --> Buffers
    Middleware --> PM

This diagram summarizes the compile time flow from DSL input to runtime execution. It also highlights the boundary between compilation, the guest runtime, and host-runtime effect handling.

Core Components

AST Module

The AST module is located in rust/language/src/ast/. It represents choreographies as data structures.

The main type is Choreography.

#![allow(unused)]
fn main() {
pub struct Choreography {
    pub name: Ident,
    pub namespace: Option<String>,
    pub roles: Vec<Role>,
    pub protocol: Protocol,
    pub attrs: HashMap<String, String>,
}
}

This struct holds the protocol name and optional namespace. It contains participating roles and the protocol tree. Metadata attributes are stored in the attrs field.

The Protocol enum defines all protocol actions.

The Protocol enum in rust/language/src/ast/protocol.rs is a recursive tree structure with variants for all DSL constructs. Key variant families include:

  • Communication: Send, Broadcast
  • Branching: Choice, Case
  • Authority: Let (with AuthorityBindingMode), Publish, PublishAuthority, Materialize, Handoff
  • Commitment lifecycle: Begin, Await, Resolve, Invalidate
  • Control flow: Rec, Var, Loop, Parallel, Timeout, DependentWork
  • Extensibility: Extension
  • Termination: End

NonEmptyVec is used where the DSL enforces at least one branch. See the source file for the full definition.

Parser Module

The parser module is located in rust/language/src/compiler/parser/. It converts DSL text into AST using the Pest parser generator.

The parser validates role declarations and builds the protocol tree from the input text. It runs a layout preprocessor before the grammar parse. This enables layout-sensitive syntax with -> message arrows, => branch arrows, choice Role at branches with leading |, and par blocks.

Two entry points are available.

#![allow(unused)]
fn main() {
pub fn parse_choreography_str(input: &str) -> Result<Choreography, ParseError>
pub fn parse_choreography_file(path: &Path) -> Result<Choreography, ParseError>
}

The parser performs syntactic validation and basic semantic checks. The file-based entry point expects the canonical .tell source extension.

Projection Module

The projection module is located in rust/language/src/compiler/projection.rs. Projection transforms a global protocol into a local view for each role.

The algorithm determines what each participant should do.

#![allow(unused)]
fn main() {
pub fn project(choreography: &Choreography, role: &Role) -> Result<LocalType, ProjectionError>
}

Projection handles merging parallel branches. It also detects conflicts between branches.

Code Generation Module

The codegen module is located in rust/language/src/compiler/codegen/. It converts local types into Rust session types and effect programs.

The generator creates compile-time type-safe protocol implementations.

#![allow(unused)]
fn main() {
pub fn generate_session_type(role: &Role, local_type: &LocalType, protocol_name: &str) -> TokenStream
pub fn generate_choreography_code(name: &str, roles: &[Role], local_types: &[(Role, LocalType)]) -> TokenStream
pub fn generate_choreography_code_with_extensions(
    choreography: &Choreography,
    local_types: &[(Role, LocalType)],
    extensions: &[Box<dyn ProtocolExtension>],
) -> TokenStream
pub fn generate_choreography_code_with_dynamic_roles(
    choreography: &Choreography,
    local_types: &[(Role, LocalType)],
) -> TokenStream
pub fn generate_choreography_code_with_namespacing(
    choreo: &Choreography,
    local_types: &[(Role, LocalType)],
) -> TokenStream
pub fn generate_choreography_code_with_annotations(
    name: &str,
    roles: &[Role],
    local_types: &[(Role, LocalType)],
    choreo: &Choreography,
) -> TokenStream
pub fn generate_choreography_code_with_topology(
    choreography: &Choreography,
    local_types: &[(Role, LocalType)],
    inline_topologies: &[InlineTopology],
) -> TokenStream
pub fn generate_dynamic_role_support(choreography: &Choreography) -> TokenStream
pub fn generate_role_implementations(
    role: &Role,
    local_type: &LocalType,
    protocol_name: &str,
) -> TokenStream
pub fn generate_topology_integration(
    choreography: &Choreography,
    inline_topologies: &[InlineTopology],
) -> TokenStream
pub fn generate_helpers(name: &str, messages: &[MessageType]) -> TokenStream
}

The generator creates session types and role structs. It supports dynamic roles including parameterized roles and runtime management.

Effect System

The effect system is located in rust/runtime/src/effects/. It decouples protocol logic from transport.

Protocols are represented as effect programs. Handlers interpret these programs.

#![allow(unused)]
fn main() {
pub trait ChoreoHandler: Send {
    type Role: RoleId;
    type Endpoint: Endpoint;

    async fn send<M: Serialize + Send + Sync>(
        &mut self, ep: &mut Self::Endpoint, to: Self::Role, msg: &M
    ) -> ChoreoResult<()>;
    async fn recv<M: DeserializeOwned + Send>(
        &mut self, ep: &mut Self::Endpoint, from: Self::Role
    ) -> ChoreoResult<M>;
    async fn choose(
        &mut self, ep: &mut Self::Endpoint, who: Self::Role, label: <Self::Role as RoleId>::Label
    ) -> ChoreoResult<()>;
    async fn offer(
        &mut self, ep: &mut Self::Endpoint, from: Self::Role
    ) -> ChoreoResult<<Self::Role as RoleId>::Label>;

    async fn with_timeout<F, T>(
        &mut self, ep: &mut Self::Endpoint, at: Self::Role, dur: Duration, body: F
    ) -> ChoreoResult<T>
    where
        F: Future<Output = ChoreoResult<T>> + Send;
}
}

Handlers implement this trait to provide different execution strategies. This async handler is distinct from the synchronous telltale_machine::model::effects::EffectHandler used by the protocol machine.

Use Effect Handlers and Session Types for protocol-machine integration guidance.

Protocol-Machine Execution Layer

The protocol machine provides a bytecode execution model for local types. The telltale-machine crate compiles LocalTypeR into bytecode and executes it with a policy-based scheduler. The telltale-simulator crate wraps guest runtimes around that protocol machine with deterministic middleware for latency, faults, property monitoring, and checkpointing.

The protocol machine maintains session state with bounded message buffers. Each coroutine references its assigned program by ID. The scheduler policies are observationally equivalent per the Lean model. Nested protocol machines can be hosted inside a coroutine for hierarchical simulation.

The choreography runtime also includes a deterministic heap utility for explicit resource tracking and heap commitments. That heap now uses a versioned canonical encoding boundary for resource identity and Merkle inputs. Its ordering and replay rules are part of the documented contract. See Resource Heap for the heap-side contract.

At the embedding boundary, the protocol machine distinguishes current host ownership from protocol typing and capability admission. Production host integrations use load_choreography_owned(...) and OwnedSession for explicit session-local authority after open. Guest runtimes embed the protocol machine inside a host runtime with explicit external handlers.

Delegation and reconfiguration paths emit explicit receipts and audit records. See Protocol Machine Architecture for details on the underlying bytecode protocol-machine architecture.

Data Flow

This section demonstrates the transformation of a choreography through each layer.

Input choreography:

protocol RequestResponse =
  roles Alice, Bob
  Alice -> Bob : Request of api.Request
  Bob -> Alice : Response of api.Response

The choreography specifies a request-response pattern.

After parsing, the AST contains a nested send structure.

#![allow(unused)]
fn main() {
Protocol::Send {
    from: Alice, to: Bob, message: Request,
    continuation: Protocol::Send {
        from: Bob, to: Alice, message: Response,
        continuation: Protocol::End
    }
}
}

This represents the global protocol tree.

After projection for Alice, the local type shows send then receive.

#![allow(unused)]
fn main() {
LocalType::Send {
    to: Bob, message: Request,
    continuation: LocalType::Receive {
        from: Bob, message: Response,
        continuation: LocalType::End
    }
}
}

Alice sends a request and waits for a response.

After projection for Bob, the local type shows receive then send.

#![allow(unused)]
fn main() {
LocalType::Receive {
    from: Alice, message: Request,
    continuation: LocalType::Send {
        to: Alice, message: Response,
        continuation: LocalType::End
    }
}
}

Bob waits for a request and sends a response.

After code generation for Alice, a session type is created.

#![allow(unused)]
fn main() {
type Alice_Protocol = Send<Bob, Request, Receive<Bob, Response, End>>;
}

This session type enforces the protocol at compile time.

At runtime, the protocol-machine and generated effect boundary drive actual communication. The older choreography-layer effect-program builder remains an internal implementation technique, not the canonical public architecture story.

Design Decisions

Why Conservation

Telltale treats protocol semantics as conserved quantities rather than emergent properties. Every runtime phenomenon reduces to the conservation framework. Async execution reduces to commitment lifecycle. Race conditions reduce to authority violations. Retry logic reduces to identity and commitment. See the full reduction table in Conservation Framework.

This design eliminates classes of bugs by construction. Hidden concurrency, authority ambiguity, silent failure, late result races, and unbounded waiting all map to violations of specific conserved properties. The erasure principle removes everything that is not part of the conserved system from the programming model.

Why Choreographic Programming

Choreographies specify the global protocol once and projection generates local code for each role. This realizes structure conservation: the compositional structure of the protocol is defined entirely by its type, and no runtime behavior can alter the protocol shape.

Why Effect Handlers

Effect handlers are the typed operational vocabulary between the protocol machine and the world. They realize commitment conservation: every effect is a tracked commitment that must resolve to a terminal class. Internal handlers realize scheduling, dispatch, and replay. External handlers realize storage, network, and domain-specific integrations. See Choreography Effect Handlers for the async surface and Effect Handlers and Session Types for the protocol-machine boundary.

Why Capability Semantics Are First Class

Telltale treats protocol-critical capability semantics as part of the model, not as an application policy layer bolted on later. Admission gates, live ownership, evidence/finalization artifacts, and transition artifacts all change protocol-visible truth or determine who may change it. They therefore belong in the runtime/Lean boundary.

The important non-goal is scope control. Telltale does not try to become a general-purpose host authorization framework. Product-specific user/resource policy remains outside the model unless it changes protocol-critical semantics and can be represented as typed runtime/Lean objects.

Why Session Types

Session types provide compile-time guarantees about protocol compliance. The Rust type system enforces that each role follows their protocol correctly. Type checking prevents message ordering and payload-shape violations at compile time. Global deadlock claims remain assumption-scoped in the theory results.

Platform Abstraction

The runtime module provides platform-specific async primitives. Native targets use tokio. WASM uses wasm-bindgen-futures. The same code runs on servers and in browsers.

Extension Points

Custom Handlers

Implement ChoreoHandler for choreography-layer transports. Implement EffectHandler for protocol-machine host integration. See Choreography Effect Handlers and Effect Handlers and Session Types for details.

Middleware

Wrap handlers with middleware for cross-cutting concerns. Logging, metrics, and retry logic can be added as middleware. Multiple middleware layers can be stacked on a single handler.

Implementation Organization

This page focuses on conceptual architecture: compilation stages, runtime execution paths, and why those boundaries exist.

For concrete workspace layout, crate dependency edges, per-crate responsibilities, and Lean constructor correspondence, see Code Organization.

Code Organization

This document describes the current implementation layout of the repository. It covers workspace structure, crate responsibilities, and the main dependency directions between crates. For the conceptual system design, see Architecture.

Workspace Layout

The repository root contains the facade crate, the Cargo workspace definition, the Lean formalization, and the documentation tree. Most Rust implementation crates live under rust/.

telltale/
├── Cargo.toml
├── rust/
│   ├── src/                root facade crate source
│   ├── types/              telltale-types
│   ├── theory/             telltale-theory
│   ├── language/           telltale-language
│   ├── macros/             telltale-macros
│   ├── runtime/            telltale-runtime
│   ├── machine/            telltale-machine
│   ├── simulator/          telltale-simulator
│   ├── viewer/             telltale-viewer
│   ├── ui/                 telltale-ui
│   ├── web/                telltale-web
│   ├── bridge/             telltale-bridge
│   ├── transport/          telltale-transport
│   └── lints/              telltale-lints
├── lean/
├── docs/
└── examples/

This layout reflects the current workspace member list in the root Cargo.toml. external-demo and external-demo-macros are excluded from the workspace. They live at the repository root as separate example crates.

Dependency Shape

The workspace is organized around a small set of stable directions. telltale-types is the shared foundation crate. telltale-language is the shared choreography frontend.

The main direct dependency directions are:

  • telltale-types is used by telltale-theory, telltale-language, telltale-machine, telltale-simulator, telltale-runtime, telltale-transport, and the root telltale crate.
  • telltale-theory is used by telltale-machine, telltale-runtime, telltale-bridge, and the root telltale crate behind features.
  • telltale-language is used by telltale-macros, telltale-runtime, and the root telltale crate.
  • telltale-macros is used by telltale-runtime and the root telltale crate.
  • telltale-machine is used by telltale-simulator, telltale-bridge, and the root telltale crate.
  • telltale-simulator is used by telltale-viewer as the authoritative source of simulation artifacts.
  • telltale-viewer is used by telltale-ui as the pure artifact/query/command layer.
  • telltale-ui is used by telltale-web as the shared portable Dioxus UI core.
  • The root telltale crate is used by telltale-runtime as a facade dependency.
  • telltale-runtime is used by telltale-bridge through optional features and by telltale-transport.

The important organizational change is that the shared frontend now lives in telltale-language. telltale-runtime is no longer the right crate to describe as the main DSL parser and projection layer.

YAML is not a supported user configuration boundary today. If a future crate accepts user-provided YAML, the parser must cap input bytes and recursion depth before deserialization and should prefer serde_yaml_ng over the increasingly stale serde_yaml crate.

Crate Responsibilities

telltale-types

telltale-types is the foundational data-model crate. It defines GlobalType, LocalTypeR, Label, PayloadSort, fixed-point numeric support, and content-addressing infrastructure. It has no dependencies on other workspace crates.

The content-addressing layer is also here. DefaultContentHasher is the central policy alias for content hashing and currently resolves to BLAKE3. The sha256 feature enables explicit SHA-256 helper types as an alternative.

telltale-theory

telltale-theory contains pure session-type algorithms. Its core responsibilities are projection support, merge, duality, well-formedness, bounded recursion strategies, subtyping, semantics, and coherence predicates. It does not own parsing or runtime execution.

This crate depends only on telltale-types. It is the algorithm layer consumed by the machine, runtime, bridge, and optional root-crate theory features. Projection memoization uses the content-addressing facilities from telltale-types.

telltale-language

telltale-language is the shared choreography frontend. It owns the DSL AST, parsing, validation, projection to frontend LocalType, code generation helpers, effect-interface scaffolding support, and integration helpers such as compile_choreography(...) and CompiledChoreography.

Downstream integrations that need validated ASTs, projected locals, ordered annotation records, or theory-facing artifact export should start here. This crate is now the canonical frontend layer for both first-party and downstream integrations. It depends on telltale-types and no other workspace crate.

telltale-macros

telltale-macros provides the procedural macro surface. It exports tell!, session, Role, Roles, and Message. It depends on telltale-language for the shared frontend behavior used by the macro expansion path.

The tell! macro is the main compile-time DSL entry point for inline choreographies. It generates the protocol module, typed effect surfaces, and projectable session surfaces where that correspondence exists. Macro correctness is guarded by operational tests rather than a mechanized proof.

telltale-machine

telltale-machine provides the protocol machine and guest-runtime surfaces. It compiles LocalTypeR into bytecode and executes it with bounded buffers, scheduler policies, observability, and protocol-machine semantic objects. It is the canonical semantic core used by the simulator and by direct host embeddings.

The main public front doors are telltale_machine::model, telltale_machine::runtime, and telltale_machine::semantics. telltale_machine::model exposes execution, effect, scheduler, state, semantic-object, and transition vocabulary. telltale_machine::runtime exposes loading, admission, and runner surfaces such as CodeImage, ProtocolMachine, GuestRuntime, and OwnedSession. telltale_machine::semantics exposes the higher-level semantic interpretation and analysis surface layered over the protocol machine core.

telltale-simulator

telltale-simulator wraps the protocol machine with deterministic testing and simulation infrastructure. It provides runner entry points, SimulationHarness, scenario parsing, field/environment hooks, fault injection, network modeling, property checks, typed persisted replay/checkpoint artifacts, replay artifacts, and distributed simulation reports.

This crate depends directly on telltale-machine and telltale-types. It is the preferred test path for third-party implementations of protocol-machine EffectHandler. Its generated-effect helpers exist as adjacent APIs behind a narrower helper boundary, but the main integration path is still SimulationHarness.

telltale-viewer

telltale-viewer is the pure artifact/model layer for the simulator webapp work. It owns viewer artifact envelopes, schema/version handling, branch patch types, search/query models, and the first application-service boundary for run inspection.

This crate is intentionally renderer-free. Browser APIs and Dioxus component concerns belong in the future telltale-web and telltale-ui layers, not here. The goal is to keep artifact loading, branch mutation requests, and historical inspection state deterministic and reusable across shells.

telltale-search is the generic weighted-graph search substrate for the workspace. It owns canonical search-machine semantics, replay artifacts, determinism and fairness capability vocabulary, explicit graph-epoch boundaries, and fairness-aware scheduler artifacts for weighted graph search. The crate is target-agnostic. The optional multi-thread feature only enables native speculative proposal execution.

This crate is intentionally downstream-oriented and domain-generic. Application-specific routing, RF, mesh, or topology policies should live in downstream crates that implement its domain traits rather than in telltale-search itself. The stable import posture is:

  • fail-closed SearchQuery::try_multi_goal(...) and SearchQuery::try_candidate_set(...) constructors
  • generic selected-result surfaces such as SelectedSolution, SearchResultBoundArtifact, and SearchResultSummary
  • SearchDomain::selected_result_candidates(...) when result admissibility is narrower than the raw built-in query adapter
  • SearchClaimClass and theorem-pack problem-class exports for admission checks
  • the validated SearchExecutionPolicy matrix rather than ad hoc scheduler knobs

Legacy route/incumbent aliases remain available through the narrow telltale_search::compat surface for migration, but they are no longer the recommended generic API boundary. SearchCachingProfile::IncrementalReuse also remains outside the stable import posture until it is implemented.

telltale-ui

telltale-ui is the portable Dioxus UI core for shared simulator tooling. It owns the global shell layout, reusable panels/cards/toolbars/badges, graph rendering primitives, viewer route state, readiness/publication diagnostics, and the task-owner helper used by long-lived UI work.

This crate is intentionally browser-free. It consumes telltale-viewer through the typed query/command boundary and renders observed projections over authoritative artifacts.

telltale-web

telltale-web is the thin browser/WASM shell around telltale-ui. It owns Dioxus.toml, index.html, Tailwind packaging, browser bootstrap, and any future browser-specific integration. The goal is to keep platform interop here and keep the reusable UI core portable.

telltale-runtime

telltale-runtime is the choreography-layer runtime crate. It provides the async ChoreoHandler path, effect interpretation, topology and transport abstractions, testing utilities, formatting tooling, and other runtime support for generated choreography code.

This crate also re-exports parts of the shared frontend for convenience, but it does not replace telltale-language as the canonical frontend layer. Its util/ module now contains platform-facing helpers such as spawn, clock, RNG, and synchronization support. Its heap/ module now includes a dedicated encoding.rs layer for canonical heap bytes and tagged hashing preimages, plus published conformance vectors in rust/runtime/tests/data/heap_vectors_v1.json.

telltale-bridge

telltale-bridge is the Rust↔Lean bridge crate. It provides export, import, schema handling, protocol-machine trace normalization, semantic-object conversion, and runner-driven cross-validation surfaces. Optional features enable CLI and runner workflows.

This crate depends directly on telltale-types and telltale-machine. It also uses telltale-theory and telltale-runtime behind optional features and in development workflows.

telltale-transport

telltale-transport provides a first-party reference TCP transport implementation. Today it is centered on TCP transport support over the choreography-layer transport abstractions from telltale-runtime. It depends on telltale-runtime and telltale-types.

This crate should be read as a transport implementation layer, not as the main protocol frontend or runtime semantic core. Its job is to demonstrate how concrete transports realize runtime transport contracts for choreography-layer systems.

telltale-lints

telltale-lints contains custom linting support built on syn, quote, and proc_macro2. It is a small support crate and does not currently define central protocol semantics. Its role is repository-specific static analysis rather than protocol execution.

telltale

The root telltale crate is the facade crate at the repository root. It re-exports session-core types, the macro surface, selected shared-frontend APIs, and optional theory features. It also exposes the root session-type library and DSL support used by many examples.

Notably, the root crate now re-exports compile_choreography(...), compile_choreography_ast(...), CompiledChoreography, ordered annotation helpers, and parse_choreography_str(...). This makes the facade crate a reasonable entry point for smaller integrations, while telltale-language remains the authoritative frontend crate.

Selected Workspace Binaries

Some important tooling surfaces are shipped as binaries inside workspace crates.

  • effect-scaffold in telltale-runtime exports generated effect-family files and simulator scaffolds.
  • choreo-fmt in telltale-runtime formats choreography sources and can expose lowering explanations.
  • lean-bridge, lean-bridge-exporter, and golden in telltale-bridge support validation and bridge workflows.
  • run and replay in telltale-simulator support scenario execution and replay.

These binaries are generated surfaces & tooling. They are not the primary conceptual entry points for the architecture itself.

Lean Correspondence

The strongest direct constructor correspondence is between telltale-types and the foundational Lean session-type definitions. That shared core covers GlobalType, LocalTypeR, Label, and PayloadSort. This is the main type-level bridge between Rust and Lean.

The current correspondence table for the shared core is:

Lean TypeRust TypeFile
GlobalType.endGlobalType::Endrust/types/src/global.rs
GlobalType.comm p q bsGlobalType::Comm { sender, receiver, branches }rust/types/src/global.rs
GlobalType.mu t GGlobalType::Mu { var, body }rust/types/src/global.rs
GlobalType.var tGlobalType::Var(String)rust/types/src/global.rs
LocalTypeR.endLocalTypeR::Endrust/types/src/local.rs
LocalTypeR.send q bsLocalTypeR::Send { partner, branches }rust/types/src/local.rs
LocalTypeR.recv p bsLocalTypeR::Recv { partner, branches }rust/types/src/local.rs
LocalTypeR.mu t TLocalTypeR::Mu { var, body }rust/types/src/local.rs
LocalTypeR.var tLocalTypeR::Var(String)rust/types/src/local.rs
PayloadSort.unitPayloadSort::Unitrust/types/src/global.rs
LabelLabel { name, sort }rust/types/src/label.rs

Lean still includes a delegate constructor in GlobalType that is not exposed in the Rust core GlobalType. That remains a known parity gap in the shared foundational type layer. The larger proof and runtime correspondence story is documented in Lean Verification and Rust-Lean Bridge and Parity.

Getting Started

Installation

Choose dependencies based on the integration path you need. For most application code using the compile-time DSL, start with the facade crate.

[dependencies]
telltale = "11.3.0"

This gives you the root session-type surface and the tell! macro. Add other crates only when you need a lower-level integration path.

Which Crate Should You Use

Use this table to pick the right entry point.

If you needUse
facade APIs plus the tell! macrotelltale
runtime parsing, validated ASTs, projection, and integration artifactstelltale-language
async choreography handlers and topology/runtime supporttelltale-runtime
pure theory algorithmstelltale-theory
protocol-machine execution in a host runtimetelltale-machine
deterministic simulation and host-handler testingtelltale-simulator
Lean JSON import, export, and validation toolstelltale-bridge
reference choreography transporttelltale-transport

The important split is between the shared frontend and the execution layers. telltale-language is the shared frontend. telltale-runtime and telltale-machine are different execution paths.

Understanding the Crates

telltale-types contains the core protocol data model. It defines GlobalType, LocalTypeR, Label, PayloadSort, and content-addressing support. Lean still includes a delegate constructor that is not exposed in the Rust core GlobalType.

telltale-language is the shared choreography frontend. It owns the AST, parser, validation, projection to frontend LocalType, ordered annotation extraction, and integration helpers such as compile_choreography(...). Use this crate when you need to parse or inspect DSL at runtime without going through macros.

telltale-runtime is the choreography-layer runtime. It provides async ChoreoHandler integration, topology support, testing helpers, and tooling such as choreo-fmt and effect-scaffold. telltale-machine provides the protocol machine and guest-runtime execution surfaces.

telltale-simulator wraps the protocol machine with deterministic middleware for testing. telltale-bridge supports Rust↔Lean conversion and validation. telltale-transport provides a first-party reference TCP transport for choreography-layer systems.

Feature Flags

The workspace uses feature flags to control optional algorithms and target support. The root crate keeps its default surface small.

telltale

FeatureDefaultDescription
test-utilsnotesting utilities
wasmnoWebAssembly support
theorynoenable telltale-theory re-exports
theory-async-subtypingnoenable asynchronous subtyping helpers
theory-boundednoenable bounded recursion helpers
fullnoenable all optional root features

telltale-theory

FeatureDefaultDescription
projectionyesglobal-to-local projection
dualityyesdual type computation
mergeyeslocal merge operations
well-formednessyestheory validation predicates
boundedyesbounded recursion strategies
async-subtypingyesasynchronous subtyping
sync-subtypingyessynchronous subtyping
semanticsyesasync step semantics
coherenceyescoherence predicates
fullnoenable all optional theory features

telltale-runtime

FeatureDefaultDescription
test-utilsnoruntime testing utilities
wasmnoWebAssembly support
native-clinobuild choreo-fmt and effect-scaffold
native-examplesnobuild runtime examples that require native tooling

telltale-bridge

FeatureDefaultDescription
runneryesenable Lean runner support
clinobuild the main bridge CLI
exporternobuild the choreography exporter binary
goldennobuild the golden-file CLI

Minimal Root Dependency

telltale = { version = "11.3.0", default-features = false }

This keeps the dependency surface small while preserving the core facade crate.

Root Crate With Theory Support

telltale = { version = "11.3.0", features = ["theory"] }

This adds the theory re-exports to the facade crate.

Runtime CLI Tooling

telltale-runtime = { version = "11.3.0", features = ["native-cli"] }

Enable this when you want binaries such as choreo-fmt or effect-scaffold. It is not required for normal library usage.

First Protocol With tell!

For compile-time protocol definitions, tell! is the main public entry point.

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol PingPong =
    roles Alice, Bob
    Alice -> Bob : Ping
    Bob -> Alice : Pong
}

assert!(PingPong::proof_status::SESSION_PROJECTABLE);
}

This defines a simple two-role protocol. The macro generates the protocol module and, when the protocol is projectable, the session surfaces for that protocol.

Runtime Parsing Without Macros

Use the shared frontend when protocol text is loaded at runtime or when another tool needs validated artifacts.

#![allow(unused)]
fn main() {
use telltale_language::compile_choreography;

let compiled = compile_choreography(
    "protocol PingPong =\n  roles Alice, Bob\n  Alice -> Bob : Ping\n  Bob -> Alice : Pong\n",
)?;

let local_types = compiled.try_local_type_r_map()?;
let global_type = compiled.try_global_type()?;
}

This path parses, validates, and projects the choreography once. It is the right entry point for downstream integrations that need AST access, ordered annotations, or theory-facing artifacts.

Core Concepts

Choreographies

A choreography describes the protocol from a global viewpoint. Projection turns that global description into per-role local behavior. This is the main abstraction behind the Telltale DSL.

Roles

Roles are the participants in the protocol. Each role sends and receives messages according to its projected local type. Generated session surfaces enforce those obligations at the type level when projection succeeds.

Messages

Messages are the data exchanged between roles. At the choreography layer, message payloads typically use serde serialization. At the protocol-machine layer, host integration works through typed effect requests and outcomes instead.

Handler Boundaries

Telltale exposes two main handler surfaces. Use ChoreoHandler for generated choreography code in telltale-runtime. Use EffectHandler for protocol-machine host integration in telltale-machine.

If you implement protocol-machine EffectHandler, validate it through SimulationHarness in telltale-simulator. That is the supported deterministic test path for host integrations.

Choreographic DSL

Overview

The parser translates the layout-sensitive Telltale DSL into the internal AST (Choreography and Protocol). The DSL is direct style. Statements are newline separated. Indentation defines blocks. Records keep braces, but structural blocks do not.

The parser lives in rust/language/src/compiler/parser/. It uses Pest plus a layout preprocessor. The canonical source-file extension for Telltale source files is .tell.

The preferred surface style mixes MPST operators with a small functional-language layout:

  • keep ->, ->*, |, rec, and continue for protocol structure
  • reserve => for branch/result bodies
  • use indentation to make control flow visually obvious
  • keep braces for record/data layout instead of general structural blocks
  • format standalone records and effect-semantics records as multiline brace blocks with key : value fields
  • use sender records like Role { priority : high }
  • use Message of module.Type with dotted paths instead of Rust-style ::

Scope

The DSL describes protocol structure, role-local communication obligations, and protocol-critical authority checks. It does not model arbitrary host state or general application ownership. Session and fragment ownership boundaries are still derived at runtime from composition and link metadata when available. Protocol-visible authority queries and evidence flow are part of the language surface.

The current DSL includes an authority-oriented surface for protocol-critical host queries and typed local branching:

  • top-level type and type alias declarations
  • nominal effect interface declarations
  • protocol-level uses declarations
  • authoritative let / observe let bindings for effect queries
  • let bindings for linear transfer receipts
  • case ... of over Result/Maybe-style constructors
  • timeout ... on timeout ... on cancel ...
  • evidence guards of the form when check Effect.op(...) yields witness

These forms participate in explicit language tiers:

  • full spec: the DSL can express the construct and the semantic object model justifies it
  • session projectable: the construct admits MPST/session projection
  • protocol-machine executable: the construct is executable through the protocol-machine path even when it is not session projectable
  • proof only: the construct parses, but lacks the metadata required for executable lowering

The generated Rust surface exposes this status through Protocol::proof_status.

DSL Syntax

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol PingPong =
    roles Alice, Bob
    Alice -> Bob : Ping
    Bob -> Alice : Pong
}
}

tell! is the canonical public DSL entrypoint. String-literal macro input and legacy brace-based structural syntax are rejected. The canonical surface is token-form, indentation-based, and proof-directed.

tell! is outside the current formal-verification claim. The formal claim currently covers the Lean models/theorems plus strict correspondence and operational compiler gates, not Rust macro expansion itself.

When a protocol projects cleanly to sessions, the generated module exposes Protocol::sessions. Every generated protocol also exposes Protocol::proof_status, which reports the strongest supported tier and any projection blocker.

Namespaces

Namespaces are expressed via a module declaration (rather than attributes) in source files:

module threshold_ceremony exposing (ThresholdProtocol)

protocol ThresholdProtocol =
  roles Coordinator, Signers[*]
  Coordinator -> Signers[*] : Request

Multiple modules can coexist in separate files. Inside tell! you typically omit the module header and define one protocol-oriented spec directly in Rust.

The parser recognizes import declarations for completeness. Import resolution is not currently applied during compilation.

Supported Constructs

1) Send Statement

Buyer { priority : high }
  -> Seller : Request of shop.Order

Seller
  -> Buyer : Quote of shop.Price

The send statement transfers a message from one role to another. Sender metadata is written as a record attached to the sender term. Typed message attachment is written as Message of module.Type.

2) Broadcast Statement

Leader ->* : Announcement

Broadcast sends a message to all other roles.

3) Choice Statement (explicit decider)

choice Client at
  | Buy when (balance > price) =>
      Client
        -> Server : Purchase of shop.Order
  | Cancel =>
      Client
        -> Server : Cancel

The deciding role (Client) selects a branch. Guards are optional. Guard expressions are parsed through a typed predicate IR and must be boolean-like. Valid examples include ready, balance > price, and is_open().

4) Loop Statement

loop decide by Client
  Client -> Server : Request
  Server -> Client : Response

This loop continues while the deciding role chooses to continue. The decide by form is desugared to a choice+recursion pattern at parse time. The first message in the loop body serves as the “continue” signal - when the deciding role sends it, the loop continues. A synthetic Done message is added as the termination signal.

The example above desugars to:

rec RoleDecidesLoop
  choice Client at
    | Request =>
      Client -> Server : Request
      Server -> Client : Response
      continue RoleDecidesLoop
    | Done =>
      Client -> Server : Done

Requirement: The first statement in a loop decide by Role body must be a send from the deciding role. This ensures the decision to continue is fused with the first message, saving a round trip compared to sending a separate “continue” signal.

This desugaring converts the RoleDecides loop into standard multiparty session type constructs (choice + recursion), keeping the surface inside the Lean-modeled theory fragment and compatible with standard MPST projection algorithms.

loop repeat 5
  A -> B : Ping
  B -> A : Pong

This loop repeats a fixed number of times. The compiler records the iteration count in the AST.

loop while "has_more_data"
  A -> B : Data

This loop parses the string content through the same typed predicate IR used by guards. The parser rejects non-boolean predicates such as "count + 1" before building the AST.

loop forever
  A -> B : Tick

This loop has no exit condition. Use it for persistent background protocols.

5) Parallel Statement

par
  | A -> B : Msg1
    B
      -> A : Ack
  | C -> D : Msg2

Parallel composition is expressed by par with leading | branches. A solitary branch is a parse error.

6) Recursion and Calls

rec Loop
  A -> B : Tick
  continue Loop

This defines a recursive label Loop and uses continue Loop to jump back, modeling unbounded recursion.

call Handshake

This calls another protocol that is in scope. The call target must be defined in the same file or where block.

The continue keyword is for recursive back-references within a rec block. The call keyword is for invoking sub-protocols defined in where blocks.

7) Protocol Composition (where block)

Define and reuse local protocol fragments inside a where block.

protocol Main =
  roles A, B, C
  call Handshake
  call DataTransfer
  A -> C : Done
where
  protocol Handshake =
    A -> B : Hello
    B -> A : Hi
  protocol DataTransfer =
    A -> B : Data
    B -> A : Ack

Local protocols can call each other and can be used within choices, loops, and branches.

8) Message Types and Payloads

The preferred typed message form is Message of module.Type. Dotted paths are preferred in DSL surface syntax.

A
  -> B : Request of api.Request
B
  -> A : Response of api.Response

A
  -> B : Msg { data : String, count : Int }
B
  -> A : Result(i : Int, ok : Bool)

This sequence demonstrates payload and result handling patterns with mixed message shapes.

9) Dynamic Role Count Support

Dynamic role counts are supported via wildcard and symbolic parameters.

protocol ThresholdProtocol =
  roles Coordinator, Signers[*]
  Coordinator -> Signers[*] : Request
  Signers[0..threshold] -> Coordinator : Response

This example uses a wildcard role family. It also uses a symbolic bound in the role index.

protocol ConsensusProtocol =
  roles Leader, Followers[N]
  Leader -> Followers[*] : Proposal
  Followers[i] -> Leader : Vote

The consensus protocol above demonstrates a symbolic role count (N) with an index variable (i) iterating over the follower set.

10) Profiles, Progress, And Proof Status

Profiles and explicit progress attachment are part of the proof-backed surface.

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  profile Replay fairness strong

  agreement_profile MembershipSoftSafe
    visibility pending
    rule threshold_participants
    usable_at provisional
    finalized_at finalized
    evidence commit_fact

  operation MembershipCheck at Coordinator progress MembershipProgress requires Replay within bounded agreement MembershipSoftSafe compose first_success =
    publish MembershipQueued

  protocol Membership under Replay =
    roles Coordinator, Member
    Coordinator -> Member : Invite
}

assert_eq!(Membership::proof_status::STRONGEST_TIER, "session_projectable");
assert!(Membership::proof_status::PROTOCOL_MACHINE_EXECUTABLE);
}

Profiles, theorem-pack requirements, and language-tier diagnostics are surfaced as generated metadata because they are part of the verified model, not an afterthought layered on top.

For effect interfaces, the canonical Rust import is use Protocol::effects. Traits, request/outcome enums, effect-domain data, and per-operation semantic metadata all live under that one boundary. Interface metadata is exposed under snake-case effect modules such as Protocol::effects::runtime.

This example mixes a named count with index variables. It enables parameterized protocols.

protocol PartialBroadcast =
  roles Broadcaster, Receivers[*]
  Broadcaster -> Receivers[0..count] : Message
  Receivers[0..threshold] -> Broadcaster : Ack

This example shows bounded ranges for role indices. It models partial broadcasts and threshold acknowledgments.

10) Timing Patterns

Timing patterns provide constructs for building time-aware protocols. They are part of the protocol-machine surface today, but they are not all session-projectable yet.

Timed Choice

A protocol-visible timeout branches the protocol on deadline expiry:

protocol TimedRequest =
  roles Client, Server
  timeout 5s Client at
    Server -> Client : Response
  on timeout =>
    Client -> Server : Cancel

Use timeout duration Role at when deadline expiry is a protocol-visible outcome. The runtime enforces the deadline. Projection and theory conversion currently reject this form until the authority-sensitive lowering rules are complete, so it is protocol-machine executable but not part of the MPST/common theory subset yet.

Durations support: ms (milliseconds), s (seconds), m (minutes), h (hours).

Liveness Pattern

The old heartbeat surface is removed from the proof-backed DSL. Model connection liveness with explicit protocol-visible timeouts plus ordinary messages:

protocol Liveness =
  roles Alice, Bob
  Alice -> Bob : Ping
  timeout 1s Bob at
    Bob -> Alice : Pong
  on timeout =>
    Bob -> Alice : Disconnect

This keeps liveness handling inside the current verified surface. If you only need an operational transport hint rather than a protocol branch, use runtime_timeout sender metadata instead.

Runtime Timeout Metadata

Record metadata can carry transport-level timeout hints:

protocol TimedOps =
  roles Client, Server
  Client { runtime_timeout : 5s }
    -> Server : Request
  Server -> Client : Response

Unlike timeout ... on timeout ..., this metadata does not change the session type. It is a hint to the transport layer and is not verified in Lean. Use it for operational timeouts when you do not want the protocol to branch on timeout.

11) Proof Bundles and Semantic Statements

proof_bundle declarations define capability sets. protocol ... requires ... selects the bundles required by a protocol.

proof_bundle DelegationBase version "1.0.0" issuer "did:example:issuer" constraint "fresh_nonce" requires [delegation, guard_tokens]
proof_bundle KnowledgeBase requires [knowledge_flow]

protocol TransferFlow requires DelegationBase, KnowledgeBase =
  roles Coordinator, Worker
  let receipt = transfer Session from Coordinator to Worker
  publish receipt as DelegationRecorded
  materialize delegationProof from DelegationRecorded
  handoff acceptInvite to Worker with receipt
  Coordinator -> Worker : Commit

The parser stores bundle declarations and required bundle names in typed choreography metadata. Bundle records include optional version, issuer, and repeated constraint fields.

Validation fails on duplicate bundles and missing required bundles. If requires is omitted and bundle coverage is unambiguous, the parser can infer required bundles from the semantic statements that remain in the DSL surface.

Legacy protocol-machine-core statements such as acquire, fork, join, delegate, and tag are removed from the proof-backed DSL and are rejected fail closed. Keep those instructions in the runtime model rather than in choreography source.

The accepted DSL still includes protocol-visible semantic statements such as publish, materialize, handoff, dependent work, begin, await, and resolve.

Runtime upgrade or reconfiguration statements are not part of the choreography DSL today. They remain explicit runtime/model surfaces until they have a real shared Rust and Lean semantics.

12) Authority and Failure Surface

The authority/failure additions keep the language expression-oriented and Elm/PureScript-like while making protocol-critical host decisions explicit.

Nominal Effect Interfaces and uses
effect Runtime
  authoritative ready : Session -> Result CommitError ReadyWitness
  {
    class : authoritative
    progress : may_block
    region : fragment
    agreement_use : required
    reentrancy : reject_same_fragment
  }
  observe watchPresence : Session -> PresenceView
  {
    class : observational
    progress : immediate
    region : session
    agreement_use : forbidden
    reentrancy : allow
  }
  command cancel : Session -> Result CancelError CancelReceipt
  {
    class : best_effort
    progress : immediate
    region : session
    agreement_use : required
    reentrancy : allow
  }

effect Audit
  command record : AuditEvent -> Unit
  {
    class : best_effort
    progress : immediate
    region : session
    agreement_use : forbidden
    reentrancy : allow
  }

protocol CommitFlow uses Runtime, Audit =
  roles Coordinator, Worker
  authoritative let readiness = check Runtime.ready(session)
  Coordinator -> Worker : Commit

Effects are nominal declarations. uses makes protocol dependencies explicit. Only declared effects named in uses may be referenced by check.

Result and Maybe with case/of
authoritative let readiness = check Runtime.ready(session) in
  case readiness of
    | Ok(witness) =>
        Coordinator -> Worker : Commit(witness)
    | Err(TimedOut) =>
        Coordinator -> Worker : Cancel

Result and Maybe are first-class sum-style forms for protocol-visible authority checks. Exhaustiveness is required for Ok/Err and Just/Nothing.

Custom Unions and Type Aliases
type CommitError =
  | TimedOut
  | Cancelled
  | InvalidWitness

type alias ReadyWitness =
  { epoch : Int, issuedBy : Role }

Use custom unions for typed failure surfaces and aliases for structured evidence payloads.

Evidence Binding with let
observe let presence = observe Runtime.watchPresence(session)
authoritative let readiness = check Runtime.ready(session)
let receipt = transfer Session from Coordinator to Worker
handoff acceptInvite to Worker with receipt

Authority queries use explicit observe let or authoritative let bindings. Linear transfer receipts still use ordinary let, but the resulting receipt is a first-class transition artifact rather than an untyped helper value.

Typed Timeout and Cancellation Blocks
timeout 5s Coordinator at
  Worker -> Coordinator : Ready(readyWitness)
on timeout =>
  Coordinator -> Worker : Cancel
on cancel =>
  Coordinator -> Worker : Cancelled

Use timeout ... on timeout ... on cancel ... when timeout and cancellation are protocol-visible outcomes rather than ambient runtime metadata.

Evidence Guards
choice Coordinator at
  | Commit when check Runtime.ready(session) yields witness =>
      Coordinator -> Worker : Commit(witness)
  | Abort =>
      Coordinator -> Worker : Abort

Evidence guards are the concise path for “take this branch only if a typed authoritative query succeeds and binds evidence”.

Linear Single-Use Bindings
let receipt = transfer Session from Coordinator to Worker
handoff acceptInvite to Worker with receipt

Bindings produced by transfer-like authority operations are linear. The compiler rejects duplicate use and implicit discard.

The first-class capability/finalization mapping for this DSL surface is:

  • authoritative and observational reads: evidence/finalization inputs
  • transfer receipts and handoffs: transition artifacts
  • publications, materialization proofs, and canonical handles: evidence-side finalization objects
Scoped Bindings with let ... in
authoritative let readiness = check Runtime.ready(session) in
  case readiness of
    | Ok(witness) =>
        Coordinator -> Worker : Commit(witness)
    | Err(reason) =>
        Coordinator -> Worker : Abort(reason)

let ... in introduces an indented statement block. Use it to keep authority logic readable without pushing protocol-critical branching back into untyped host code.

No Implicit Default Branches
authoritative let readiness = check Runtime.ready(session) in
  case readiness of
    | Ok(witness) =>
        Coordinator -> Worker : Commit(witness)
    | Err(TimedOut) =>
        Coordinator -> Worker : Cancel

Protocol-critical authority flows do not allow implicit wildcard/default masking for Result and Maybe. Missing cases are validation errors.

Typed External Query Surface
authoritative let readiness = check Runtime.ready(session)

check Effect.op(...) is the canonical language-level form for typed host queries, and it must be bound with authoritative let. Observation-only queries use observe let value = observe Effect.op(...). These user-facing forms lower to the same typed runtime/effect boundary used by the host bridge.

Choosing Between Timeout Metadata and Timeout Branching

Use runtime_timeout : 5s sender metadata for operational transport hints that do not change the protocol. Use timeout ... on timeout ... on cancel ... when the timeout result must be explicit in the protocol and replay/audit surface.

This lowering preserves statement order and continuation structure. Projection skips extension-local behavior for now and continues projecting the remaining protocol.

13) Child-Effect Aggregation

compose ... is a secondary child-effect aggregation clause on an operation. It does not define distributed agreement by itself. Agreement lives in named agreement profiles. Child-effect aggregation only says how sibling child effects roll up underneath that parent agreement.

agreement_profile PendingPublication
  visibility pending
  rule threshold_participants
  usable_at provisional
  finalized_at finalized
  evidence publication

operation syncMembership(channel : ChannelId) at Worker progress MembershipProgress requires Replay within bounded agreement PendingPublication prestate ChannelMembership compose threshold_success(2) =
  publish SyncQueued(channel)

The compose threshold_success(2) clause attaches a child-effect rollup policy to the operation without overriding the parent agreement profile.

Reusable Domain Agreement Profiles

Domain systems should define their own agreement vocabulary as named profiles over Telltale’s generic core instead of expecting core built-ins such as quorum or fallback to carry all the meaning.

agreement_profile ContactCeremonySoftSafe
  visibility pending
  rule aura_soft_safe
  usable_at soft_safe
  finalized_at finalized
  evidence commit_fact

operation addContact(contactId : String) at Coordinator progress ContactProgress requires Replay within bounded agreement ContactCeremonySoftSafe prestate ContactContext compose threshold_success(3) =
  publish ContactQueued(contactId)

This keeps Telltale generic:

  • visibility, usable_at, finalized_at, and evidence are the core semantic vocabulary
  • ContactCeremonySoftSafe and aura_soft_safe are domain-defined names
  • compose ... only describes child-effect rollup below the agreement contract, not the agreement contract itself

The generated Rust surface preserves those domain names through Protocol::agreements and Protocol::proof_status, so downstream host code can keep a natural domain vocabulary without forking the core language model.

14) Role Sets and Topologies

Role sets and topologies are declared at the top level and stored as typed metadata.

role_set Signers = Alice, Bob, Carol
role_set Quorum = subset(Signers, 0..2)
cluster LocalCluster = Signers, Quorum
ring RingNet = Alice, Bob, Carol
mesh FullMesh = Alice, Bob, Carol

These declarations do not change core protocol semantics. They provide structured topology context for tooling and simulation setup.

15) Lowering Diagnostics

Use choreo-fmt --explain-lowering to inspect canonical lowering output.

choreo-fmt --explain-lowering protocol.tell

The report includes proof-bundle metadata, inferred requirements, lowered protocol shape, and lint suggestions.

16) String-based Protocol Definition

#![allow(unused)]
fn main() {
use telltale_runtime::compiler::parser::parse_choreography_str;

let protocol = r#"
protocol PingPong =
  roles Alice, Bob
  Alice -> Bob : Ping
  Bob -> Alice : Pong
"#;

let choreography = parse_choreography_str(protocol)?;
}

This parses a string into a Choreography AST. It is the entry point for runtime parsing.

Namespaced protocols are expressed with a module header.

#![allow(unused)]
fn main() {
let protocol = r#"
module secure_messaging exposing (EncryptedProtocol)

protocol EncryptedProtocol =
  roles Sender, Receiver
  Sender -> Receiver : SecureMessage
"#;

let choreography = parse_choreography_str(protocol)?;
}

The parser builds the AST for projection, validation, and code generation.

Implementation Details

Parser Stack

  • Layout preprocessor converts indentation into explicit block delimiters.
  • Pest grammar parses the canonical indentation-based syntax after layout normalization.
  • Parser module constructs the AST and runs validation.

Parse Pipeline

  1. Preprocess layout (indentation -> {}/()).
  2. Parse with Pest grammar.
  3. Build statements and normalize par branches into Parallel.
  4. Resolve call references and lower protocol-machine-core statements to Protocol::Extension.
  5. Build Choreography and attach typed proof-bundle metadata.
  6. Run semantic checks with choreography.validate() when required by your integration.

API

Primary Functions

parse_choreography_str parses a DSL string into a Choreography AST.

#![allow(unused)]
fn main() {
use telltale_runtime::compiler::parser::parse_choreography_str;

let choreo = parse_choreography_str(r#"
protocol Example =
  roles A, B
  A -> B : Hello
"#)?;
}

This example parses a DSL string into a Choreography. It uses parse_choreography_str directly.

parse_choreography_file parses a DSL file from disk. It expects the canonical .tell source extension.

#![allow(unused)]
fn main() {
use std::path::Path;
use telltale_runtime::compiler::parser::parse_choreography_file;

let choreo = parse_choreography_file(Path::new("protocol.tell"))?;
}

This example parses a file path into a Choreography. The file must contain a valid DSL definition.

parse_dsl is an alias for parse_choreography_str.

Error Handling

#![allow(unused)]
fn main() {
match parse_choreography_str(input) {
    Ok(choreo) => { /* use choreography */ }
    Err(ParseError::UndefinedRole { role, .. }) => {
        eprintln!("Role '{}' used but not declared", role);
    }
    Err(ParseError::DuplicateRole { role, .. }) => {
        eprintln!("Role '{}' declared multiple times", role);
    }
    Err(ParseError::Syntax { span, message }) => {
        eprintln!("Syntax error at {}:{}: {}", span.line, span.column, message);
    }
    Err(e) => {
        eprintln!("Parse error: {}", e);
    }
}
}

This pattern matches common parse errors. It formats diagnostics with the reported context.

Grammar Details

Tokens and Keywords

  • Identifiers: [a-zA-Z][a-zA-Z0-9_]*
  • Integers: [0-9]+
  • Strings: "..." (used in loop while)
  • Keywords: protocol, roles, case, of, choice, at, loop, decide, by, repeat, while, forever, par, rec, continue, call, where, module, import, exposing, proof_bundle, requires, acquire, release, fork, join, abort, transfer, delegate, tag, check, using, into, as, to, from, with, bundle, version, issuer, constraint, role_set, subset, cluster, ring, mesh, let, in, type, alias, effect, uses, timeout, on, cancel, when, yields, heartbeat, every, on_missing, body, observe, authoritative, command, Ok, Err, Just, Nothing, Result, Maybe, Unit, publish, handoff, dependent, work, required, for, fragment, operation, within, guest, runtime, entry
  • Operators: ->, ->*, <->, :, =>, =, .., {}, (), [], ,, |

Comments

Single-line comments use --. Multi-line comments use {- ... -}.

-- This is a single-line comment

{- This is a
   multi-line comment -}

These comments are ignored by the parser. They use the same syntax as Haskell and PureScript.

Whitespace and Layout

Indentation defines blocks. Use {} to force an empty block or to opt out of layout. Parenthesized blocks must be non-empty.

Validation

The parser validates role declarations and parallel branch structure. choreography.validate() also validates proof-bundle declarations, required bundle references, and protocol-machine-core capability coverage.

Error Messages

Example undefined role error:

Undefined role 'Charlie'
  --> input:5:14
    |
  5 |     Alice -> Charlie : Hello
                   ^^^^^^^

This error indicates a role that was not declared in roles. The location points to the undefined identifier.

Example duplicate role error:

Duplicate role declaration 'Alice'
  --> input:3:33
    |
  3 |     roles Alice, Bob, Charlie, Alice
                                      ^^^^^

This error indicates that a role name appears more than once. The location points to the duplicate entry.

Examples

Simple Two-Party Protocol

protocol PingPong =
  roles Alice, Bob
  Alice
    -> Bob : Ping
  Bob
    -> Alice : Pong

This example shows a simple two role protocol. It uses a single send and reply.

Protocol with Choice

protocol Negotiation =
  roles Buyer, Seller

  Buyer
    -> Seller : Offer

  choice Seller at
    | Accept =>
        Seller
          -> Buyer : Accept
    | Reject =>
        Seller
          -> Buyer : Reject

This example shows an explicit choice decided by Seller. Each branch starts with a send from the deciding role.

Complex E-Commerce Protocol

protocol ECommerce =
  roles Buyer, Seller, Shipper

  Buyer
    -> Seller : Inquiry of commerce.Inquiry
  Seller
    -> Buyer : Quote of commerce.Quote

  choice Buyer at
    | Order =>
        Buyer
          -> Seller : Order of commerce.Order
        Seller
          -> Shipper : ShipRequest of logistics.ShipRequest
        Shipper
          -> Buyer : Tracking of logistics.Tracking

        loop decide by Buyer
          Buyer
            -> Shipper : StatusCheck
          Shipper
            -> Buyer : StatusUpdate

        Shipper
          -> Buyer : Delivered
        Buyer
          -> Seller : Confirmation
    | Cancel =>
        Buyer
          -> Seller : Cancel

This example combines choice and looping. It models a longer interaction with a buyer controlled loop.

Parallel Example

protocol ParallelDemo =
  roles A, B, C, D
  par
    | A -> B : Msg1
    | C -> D : Msg2

This example uses par with leading | branches. Each branch defines a parallel sub protocol. par remains protocol-machine executable and session-projectable, but it is not part of the theory-convertible subset tracked by language_tier_status().

Integration

With Projection

#![allow(unused)]
fn main() {
use telltale_runtime::compiler::{parser, projection};

let choreo = parser::parse_choreography_str(input)?;

for role in &choreo.roles {
    let local_type = projection::project(&choreo, role)?;
    println!("Local type for {}: {:?}", role.name(), local_type);
}
}

This projects the global protocol to a local type for each role. The result can be used for type driven code generation.

With Code Generation

#![allow(unused)]
fn main() {
use telltale_runtime::compiler::{parser, projection, codegen};

let choreo = parser::parse_choreography_str(input)?;
let mut local_types = Vec::new();

for role in &choreo.roles {
    let local_type = projection::project(&choreo, role)?;
    local_types.push((role.clone(), local_type));
}

let code = codegen::generate_choreography_code(
    &choreo.name.to_string(),
    &choreo.roles,
    &local_types,
);
}

This generates Rust code for the choreography. The generated code includes session types and role APIs.

Testing

Run parser tests with:

cargo test --package telltale-runtime parser

This runs the parser test suite for the choreography crate. It exercises grammar and layout handling.

Choreographic Projection Patterns

The projection algorithm transforms global choreographic protocols into local session types. Each participating role receives their own local session type. The algorithm supports patterns beyond basic send and receive operations.

Supported Patterns

The snippets below are schematic. The AST uses NonEmptyVec and Ident values, which are omitted for readability.

1. Basic Send/Receive

The global protocol specifies a send operation.

#![allow(unused)]
fn main() {
Protocol::Send {
    from: alice,
    to: bob,
    message: Data,
    continuation: End,
}
}

This defines Alice sending to Bob.

Alice’s projection shows a send operation.

#![allow(unused)]
fn main() {
LocalType::Send {
    to: bob,
    message: Data,
    continuation: End,
}
}

Alice sends Data to Bob.

Bob’s projection shows a receive operation.

#![allow(unused)]
fn main() {
LocalType::Receive {
    from: alice,
    message: Data,
    continuation: End,
}
}

Bob receives Data from Alice.

Charlie’s projection shows no participation.

#![allow(unused)]
fn main() {
LocalType::End
}

Charlie is uninvolved in this exchange.

2. Communicated Choice

The global protocol specifies a choice.

#![allow(unused)]
fn main() {
Protocol::Choice {
    role: alice,
    branches: vec![
        Branch {
            label: "yes",
            protocol: Send { from: alice, to: bob, ... },
        },
        Branch {
            label: "no",
            protocol: Send { from: alice, to: bob, ... },
        },
    ],
}
}

Alice chooses between yes and no branches.

Alice’s projection shows selection.

#![allow(unused)]
fn main() {
LocalType::Select {
    to: bob,
    branches: vec![
        ("yes", ...),
        ("no", ...),
    ],
}
}

Alice selects one branch and communicates it to Bob.

Bob’s projection shows branching.

#![allow(unused)]
fn main() {
LocalType::Branch {
    from: alice,
    branches: vec![
        ("yes", ...),
        ("no", ...),
    ],
}
}

Bob waits for Alice’s choice.

3. Local Choice

Local choice supports branches that do not start with send operations. This allows for local decisions without communication.

The global protocol defines a local choice.

#![allow(unused)]
fn main() {
Protocol::Choice {
    role: alice,
    branches: vec![
        Branch {
            label: "option1",
            protocol: End,
        },
        Branch {
            label: "option2",
            protocol: End,
        },
    ],
}
}

No send operation appears in the branches.

Alice’s projection shows local choice.

#![allow(unused)]
fn main() {
LocalType::LocalChoice {
    branches: vec![
        ("option1", End),
        ("option2", End),
    ],
}
}

Alice makes a local decision.

The key difference is between LocalChoice and Select. The Select variant indicates communicated choice where the selection is sent to another role. The LocalChoice variant indicates local decision with no communication.

The standard DSL requires choices to start with a send from the deciding role, so LocalChoice does not appear in normal projections. It is available when building the AST programmatically or via extensions.

4. Loop with Conditions

Loop conditions are preserved in the projected local types.

The global protocol includes a loop.

#![allow(unused)]
fn main() {
Protocol::Loop {
    condition: Some(Condition::Count(5)),
    body: Send { from: alice, to: bob, ... },
}
}

The loop executes 5 times.

Alice’s projection preserves the condition.

#![allow(unused)]
fn main() {
LocalType::Loop {
    condition: Some(Condition::Count(5)),
    body: Send { to: bob, ... },
}
}

The count condition is maintained.

Supported conditions include Condition::Count(n) for fixed iteration count. Custom boolean expressions use Condition::Custom(expr). Infinite loops use None and must be terminated externally.

The AST also includes Fuel, YieldAfter, and YieldWhen for tooling or extensions.

Condition::RoleDecides(role) loops specify that a role controls iteration. During projection, the condition is preserved in the local type and merge comparisons check that both sides use the same deciding role.

5. Parallel Composition

Parallel merging includes conflict detection.

Compatible parallel composition has no conflicts.

#![allow(unused)]
fn main() {
Protocol::Parallel {
    protocols: vec![
        Send { from: alice, to: bob, ... },
        Send { from: alice, to: charlie, ... },
    ],
}
}

Different recipients avoid conflicts.

Alice’s projection merges the operations.

#![allow(unused)]
fn main() {
LocalType::Send {
    to: bob,
    continuation: Send {
        to: charlie,
        continuation: End,
    },
}
}

Operations are merged into one local type in a deterministic traversal order. This merged order is a projection artifact rather than a scheduling guarantee. Branch labels in merged Branch nodes are canonicalized in label order to keep output deterministic.

Conflicting parallel composition causes errors.

#![allow(unused)]
fn main() {
Protocol::Parallel {
    protocols: vec![
        Send { from: alice, to: bob, ... },
        Send { from: alice, to: bob, ... },
    ],
}
}

Same recipient creates a conflict.

Alice’s projection fails.

#![allow(unused)]
fn main() {
Err(ProjectionError::InconsistentParallel)
}

Cannot send to same recipient in parallel.

Conflict detection rules prevent invalid projections. Multiple sends to the same role create a conflict. Multiple receives from the same role create a conflict.

Multiple selects to the same role create a conflict. Multiple branches from the same role create a conflict. Operations on different roles are allowed.

Projection Rules Summary

Chooser’s View

When all branches start with send operations, the projection is Select. This indicates communicated choice. When branches do not start with send operations, the projection is LocalChoice. This indicates local decision.

Receiver’s View

When the role receives the choice, the projection is Branch. When the role is not involved, continuations are merged.

Merge Semantics

When a role is not involved in a choice, the projections of all branches must be merged. The merge algorithm follows precise semantics based on whether the role sends or receives.

Send merge requires identical label sets. A non-participant cannot choose which message to send based on a choice they did not observe. If a role sends different messages in different branches, projection fails. Receive merge unions label sets because a non-participant can receive any message regardless of which branch was taken.

This distinction is critical for protocol safety. The Rust implementation matches the Lean formalization in lean/Choreography/Projection/. Projection parity checks are exercised from rust/bridge/tests/projection_runner_tests.rs.

Parallel Composition

When a role appears in zero branches, the projection is End. When a role appears in one branch, that projection is used. When a role appears in two or more branches, projections are merged if compatible. An error occurs if conflicts exist.

Dynamic Role Projection

The default project() entry point does not resolve runtime role families on its own. Unresolved symbolic, wildcard, or runtime role parameters return projection errors.

The projection error surface includes:

  • ProjectionError::DynamicRoleProjection
  • ProjectionError::UnboundSymbolic
  • ProjectionError::WildcardProjection
  • ProjectionError::RangeProjection

Projection still enforces core structural rules before interpretation. If a choreography uses parameterized roles, bind those roles to a concrete participant set before building executable effect programs.

Authority-Oriented Projection Status

The authority/failure additions in the DSL are intentionally split between language validation and MPST projection.

SurfaceCurrent projection statusReason
let binding = exprprojected through to continuationlocal helper form with no direct session action
linear let receipt = transfer ...validated before projectionsingle-use guarantees are enforced before local type generation
nominal effect declarationsnot projected directlydescribe external obligations, not local communication actions
protocol ... uses ...validation-only metadataconstrains which effects may be referenced
check Effect.op(...) in local expressionsvalidation-only until loweredremains a typed external query, not a local type action by itself
case ... ofprojected directlyconstructor labels become case-local branch labels; divergent senders become LocalChoice, receivers become Branch
timeout ... on timeout ... on cancel ...projected directlytimeout owners/participants receive LocalType::Timeout { body, on_timeout, on_cancel }
when check ... yields witness guardsprojected directlyevidence guards constrain validation, then projection proceeds structurally through the guarded branch bodies
publish ..., publish ... as ..., materialize ..., handoff ..., dependent work ...projected through to continuationsemantic wrapper forms do not add a session action of their own

Current fail-closed rule:

  • if a choreography relies on authority constructs that do not yet have an explicit projection rule such as begin, await, resolve, or invalidate, projection stops with a validation error instead of guessing a local-session encoding

Current linearity rule:

  • linear transfer/receipt bindings must be consumed exactly once before projection continues
  • duplicate use and implicit discard are rejected before local type generation

This keeps the parser/AST surface ahead of the proven MPST subset without creating silent projection behavior.

Implementation Notes

LocalType Variants

The enhanced projection algorithm uses these LocalType variants.

#![allow(unused)]
fn main() {
pub enum LocalType {
    Send { to, message, continuation },
    Receive { from, message, continuation },
    Select { to, branches },
    Branch { from, branches },
    LocalChoice { branches },
    Loop { condition, body },
    Rec { label, body },
    Var(label),
    Timeout { duration, body, on_timeout, on_cancel },
    End,
}
}

Each variant represents a different local type pattern. For authority-level timeout choreography, the projection now preserves the happy-path body plus the explicit timeout and cancellation branches in the local AST. Rust session-type code generation still erases those timeout arms to the body because timeout handling remains a runtime concern.

Code Generation

The generate_type_expr function in rust/language/src/compiler/codegen/mod.rs handles all variants. This includes LocalChoice, Loop, and Timeout. Code generation transforms local types into Rust session types. Timeout annotations are currently ignored in the type expression.

Dynamic roles use specialized code generation via generate_choreography_code_with_dynamic_roles. This function includes runtime role binding. Validation occurs at choreography initialization. Generated code supports dynamic role counts.

DSL Extensions

This document explains how to extend the choreography DSL with runtime effects and syntax extensions.

Overview

The extension system has two parts. Runtime extensions add type-safe effects that can be inserted into Program sequences. Syntax extensions add new grammar rules and statement parsers to the DSL.

Runtime effect extensions are projected to roles during compilation and dispatched by interpret_extensible at runtime. Syntax extensions are registered through the parser and grammar composition APIs.

The repository contains two extension registries with the same name:

  • effects::registry::ExtensionRegistry<E, R> for runtime extension effect handlers.
  • extensions::ExtensionRegistry for DSL grammar and parser extensions.

Simulator Integration for Extensions

Extension projects often need protocol-machine-level regression tests in addition to parser tests. Use telltale-simulator harness APIs to run projected local types under scenario middleware. This keeps extension validation aligned with protocol-machine effect contracts.

#![allow(unused)]
fn main() {
let harness = SimulationHarness::new(&DirectAdapter::new(&handler));
let result = harness.run(&spec)?;
assert_contracts(&result, &ContractCheckConfig::default())?;
}

This pattern makes extension runtime checks reusable across projects. See Simulation Overview for harness config files and preset constructors.

Runtime Effect Extensions

ExtensionEffect Trait

Extensions implement ExtensionEffect and specify which roles participate.

#![allow(unused)]
fn main() {
pub trait ExtensionEffect<R: RoleId>: Send + Sync + Debug {
    fn type_id(&self) -> TypeId;
    fn type_name(&self) -> &'static str;
    fn participating_roles(&self) -> Vec<R> { vec![] }
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
    fn clone_box(&self) -> Box<dyn ExtensionEffect<R>>;
}
}

The default participating_roles implementation returns an empty vector, which makes the extension global. A non-empty vector limits the extension to specific roles.

Defining an Extension

#![allow(unused)]
fn main() {
#[derive(Clone, Debug)]
pub struct ValidateCapability<R> {
    pub role: R,
    pub capability: String,
}

impl<R: RoleId> ExtensionEffect<R> for ValidateCapability<R> {
    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }

    fn type_name(&self) -> &'static str {
        "ValidateCapability"
    }

    fn participating_roles(&self) -> Vec<R> {
        vec![self.role]
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn clone_box(&self) -> Box<dyn ExtensionEffect<R>> {
        Box::new(self.clone())
    }
}
}

This extension only appears in the projection for role. Use an empty vector to make it global.

Extension Registry

Handlers register extension logic in an ExtensionRegistry.

#![allow(unused)]
fn main() {
let mut registry = ExtensionRegistry::new();
registry.register::<ValidateCapability<Role>, _>(|_ep, ext| {
    Box::pin(async move {
        let validate = ext
            .as_any()
            .downcast_ref::<ValidateCapability<Role>>()
            .ok_or_else(|| ExtensionError::TypeMismatch {
                expected: "ValidateCapability",
                actual: ext.type_name(),
            })?;
        tracing::info!(cap = %validate.capability, "checked capability");
        Ok(())
    })
})?;
}

The register method returns an error on duplicate handlers. Use ExtensionRegistry::merge to compose registries across modules.

Error Handling

Extension errors surface as ExtensionError.

#![allow(unused)]
fn main() {
pub enum ExtensionError {
    UnknownExtension { type_name: &'static str, type_id: TypeId },
    HandlerNotRegistered { type_name: &'static str },
    ExecutionFailed { type_name: &'static str, error: String },
    TypeMismatch { expected: &'static str, actual: &'static str },
    DuplicateHandler { type_name: &'static str },
    MergeConflict { type_name: &'static str },
}
}

Handlers should register all required extensions before interpretation. This keeps failures at startup instead of in the middle of protocol execution.

Interpreter Integration

Use interpret_extensible with handlers that implement ExtensibleHandler.

#![allow(unused)]
fn main() {
let mut handler = MyHandler::new();
let mut endpoint = ();
let result = interpret_extensible(&mut handler, &mut endpoint, program).await?;
}

The non-extensible interpret function does not dispatch extensions. That runtime is now an internal choreography-layer surface, not the primary public entrypoint for application code.

Using Extensions in Programs

Extensions should be introduced from the DSL/parser side so they remain part of the validated semantic surface. Do not teach or depend on manual Program::ext construction as the public modeling path.

Syntax Extensions

GrammarExtension Trait

Grammar extensions provide Pest rules and a list of statement rules they handle.

#![allow(unused)]
fn main() {
pub trait GrammarExtension: Send + Sync + Debug {
    fn grammar_rules(&self) -> &'static str;
    fn statement_rules(&self) -> Vec<&'static str>;
    fn priority(&self) -> u32 { 100 }
    fn extension_id(&self) -> &'static str;
}
}

The priority value resolves conflicts when multiple extensions define the same rule. Higher priority wins and conflicts are recorded by the registry.

StatementParser and ProtocolExtension

Statement parsers translate matched rules into protocol extensions.

#![allow(unused)]
fn main() {
pub trait StatementParser: Send + Sync + Debug {
    fn can_parse(&self, rule_name: &str) -> bool;
    fn supported_rules(&self) -> Vec<String>;
    fn parse_statement(
        &self,
        rule_name: &str,
        content: &str,
        context: &ParseContext,
    ) -> Result<Box<dyn ProtocolExtension>, ParseError>;
}
}

ProtocolExtension instances participate in validation, projection, and code generation.

#![allow(unused)]
fn main() {
pub trait ProtocolExtension: Send + Sync + Debug {
    fn type_name(&self) -> &'static str;
    fn mentions_role(&self, role: &Role) -> bool;
    fn validate(&self, roles: &[Role]) -> Result<(), ExtensionValidationError>;
    fn project(&self, role: &Role, context: &ProjectionContext) -> Result<LocalType, ProjectionError>;
    fn generate_code(&self, context: &CodegenContext) -> proc_macro2::TokenStream;
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
    fn type_id(&self) -> TypeId;
}
}

Use these traits to attach new DSL constructs to projection and codegen.

ExtensionRegistry for Syntax

The ExtensionRegistry stores grammar extensions and statement parsers.

#![allow(unused)]
fn main() {
let mut registry = ExtensionRegistry::new();
registry.register_grammar(MyGrammarExtension)?;
registry.register_parser(MyStatementParser, "my_parser".to_string());
}

The registry tracks rule conflicts and supports dependency checks. Use get_detailed_conflicts for human-readable conflict reports.

GrammarComposer

GrammarComposer combines the base grammar with registered extension rules and caches the result.

#![allow(unused)]
fn main() {
let mut composer = GrammarComposer::new();
composer.register_extension(MyGrammarExtension)?;
let grammar = composer.compose()?;
}

The cache avoids recomposing the grammar when the extension set has not changed.

ExtensionParser

ExtensionParser wires the grammar composer into the parsing pipeline.

#![allow(unused)]
fn main() {
let mut parser = ExtensionParser::new();
parser.register_extension(MyGrammarExtension, MyStatementParser)?;
let choreography = parser.parse_with_extensions(source)?;
}

The parse_with_extensions method now composes the grammar metadata and routes registered statement rules through the shared extension registry. Supported extension statements are parsed deterministically into Protocol::Extension nodes instead of falling back to the standard parser unchanged.

Extension Discovery

The extensions::discovery module provides metadata and dependency management.

#![allow(unused)]
fn main() {
let mut discovery = ExtensionDiscovery::new();
discovery.add_search_path("./extensions");
let registry = discovery.create_registry(&["timeout".to_string()])?;
}

Discovery assembles an ExtensionRegistry in dependency order and performs basic validation.

Complete Workflow Example

This section shows an end-to-end workflow for building and running a runtime extension.

Step 1: Add Dependencies

Add the choreography crate and an async runtime.

[dependencies]
telltale-runtime = "11.3.0"
tokio = { version = "1", features = ["full"] }

Use a path dependency only for local workspace development. External projects should pin to a release version.

Step 2: Define Roles and Messages

Define roles, labels, and messages for your protocol.

#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};
use telltale_runtime::{LabelId, RoleId, RoleName};

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Role { Client, Server }

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Label { Ok }

impl LabelId for Label {
    fn as_str(&self) -> &'static str {
        match self { Label::Ok => "ok" }
    }
    fn from_str(label: &str) -> Option<Self> {
        match label { "ok" => Some(Label::Ok), _ => None }
    }
}

impl RoleId for Role {
    type Label = Label;
    fn role_name(&self) -> RoleName {
        match self {
            Role::Client => RoleName::from_static("Client"),
            Role::Server => RoleName::from_static("Server"),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
enum Message { Ping, Pong }
}

Roles implement RoleId. Labels implement LabelId. Messages are serializable.

Step 3: Build an Extensible Handler

Register extension handlers and implement ExtensibleHandler.

#![allow(unused)]
fn main() {
struct DomainHandler {
    registry: ExtensionRegistry<(), Role>,
}

impl DomainHandler {
    fn new() -> Self {
        let mut registry = ExtensionRegistry::new();
        registry
            .register::<ValidateCapability<Role>, _>(|_ep, ext| {
                Box::pin(async move {
                    let validate = ext
                        .as_any()
                        .downcast_ref::<ValidateCapability<Role>>()
                        .ok_or(ExtensionError::TypeMismatch {
                            expected: "ValidateCapability",
                            actual: ext.type_name(),
                        })?;
                    tracing::info!(cap = %validate.capability, "validated");
                    Ok(())
                })
            })
            .expect("register extension");
        Self { registry }
    }
}
}

ExtensionRegistry stores handlers for each extension type. The interpret_extensible entry point uses this registry during execution.

Step 4: Build and Run a Program

Extensions remain available in the low-level choreography runtime via interpret_extensible, but that path is internal. The public modeling path is to parse/validate extension-bearing DSL and lower it through the canonical semantic surface rather than hand-assembling extension programs.

Testing

Unit tests can validate registry setup and projection behavior.

#![allow(unused)]
fn main() {
#[test]
fn test_registry() {
    let handler = DomainHandler::new();
    assert!(handler
        .extension_registry()
        .is_registered::<ValidateCapability<Role>>());
}
}

Use integration tests to check end-to-end protocol behavior with interpret_extensible.

Built-In Extensions

The extensions::timeout module contains a sample grammar extension, parser, and protocol extension. It is intended as a reference implementation and currently uses simplified parsing logic.

Examples

This document points to the example programs and common usage patterns. Each example demonstrates a specific protocol shape or runtime feature. The repo intentionally has two example families:

  • projection examples, where tell! demonstrates global protocol structure and projected session surfaces
  • generated-interface examples, where effect declarations are the source of truth for the Rust host boundary

Example Index

All examples compile against the workspace. Use the projection examples when you want to inspect session projection and endpoint code. Use the generated-interface examples when you want to follow the target architecture: protocol-visible orchestration in Telltale, host realization in Rust.

Protocol examples in examples/protocols/:

  • adder.rs: recursive two-party adder
  • alternating_bit.rs: reliable delivery with ACK branching
  • double_buffering.rs: producer-consumer coordination via tell!
  • fft.rs: eight-role FFT butterfly network
  • ring.rs: three-node ring topology via tell!
  • ring_choice.rs: ring with per-hop branching and infinite types
  • ring_max.rs: ring maximum with broadcast announcement
  • three_adder.rs: three-party sum via tell!

Effect-boundary examples in examples/effects/:

  • client_server_log.rs: logging decisions at the host boundary
  • commitment_lifecycle.rs: commitment, profile-driven progress, and agreement/finality metadata
  • elevator.rs: authority handoff and publication metadata
  • generated_effect_interfaces.rs: canonical generated Rust effect traits and semantic metadata
  • map_reduce.rs: structured fan-out/fan-in work with host compute boundaries
  • oauth.rs: authentication and authorization decisions at the effect boundary
  • reactive_signal.rs: reactive signal subscription/current-value/publish interface
  • three_buyers.rs: pricing and affordability decisions at the host boundary
  • travel_agency.rs: evidence binding, typed failure with case/of, and timeout
  • wasm/: browser integration via generated effects

Theory examples in examples/theory/:

  • async_subtyping.rs: async-subtyping checks and subtype relation examples
  • bounded_recursion.rs: bounded recursion strategies with configurable depth

Runtime examples in rust/runtime/examples/:

  • authority_surface.rs: inspect effect declarations and proof-backed parser metadata
  • choreography_tour.rs: comprehensive DSL feature tour (modules, choice, dynamic roles, ranges, recursion, projection)
  • macro_choreography.rs: programmatic choreography construction with the algebraic effect API
  • extension_example.rs: DSL extension system with custom grammar rules and parsers
  • extension_capability.rs: role-specific capability validation extension
  • extension_logging.rs: basic logging extension for choreography execution
  • extension_workflow.rs: complete workflow with multiple extension types (native-examples)
  • topology_integration.rs: local and explicit placement topology configuration
  • parameterized_roles_full.rs: parameterized roles test suite (concrete arrays, indexed access, symbolic params)
  • roles_parameterized.rs: parameterized roles parsing without proc macro
  • debug_param_roles.rs: debugging utility for parameterized role parsing
  • error_demo.rs: enhanced error reporting with span information
  • tcp_ping_pong.rs: TCP transport for real network communication (native-examples)
  • telltale_client_server.rs and three_party_negotiation.rs: runtime-oriented examples (native-examples)

Common Patterns

The following patterns cover the core protocol constructs for projection examples. tell! parses the DSL at compile time, validates the proof-backed surface, and emits sessions only when the protocol is session-projectable.

Request Response

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol RequestResponse =
    roles Client, Server
    Client -> Server : Request of api.Request
    Server -> Client : Response of api.Response
}
}

Each message line declares a sender, receiver, label, and optional payload type. Projection produces one local session type per role when RequestResponse::proof_status::SESSION_PROJECTABLE is true.

Choice

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol ChoicePattern =
    roles Client, Server
    choice Server at
      | Accept =>
          Server -> Client : Confirmation
      | Reject =>
          Server -> Client : Rejection
}
}

Only the deciding role selects the branch. Other roles react to that choice. The choice at clause names the selecting participant. Each branch label must be distinct within the choice block.

Loops

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol LoopPattern =
    roles Client, Server
    loop repeat 5
      Client -> Server : Request
      Server -> Client : Response
}
}

Use bounded loops for batch workflows or retries. The repeat count sets an upper iteration limit. The body runs sequentially within each iteration.

Parallel Branches

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  protocol ParallelPattern =
    roles Coordinator, Worker1, Worker2
    par
      | Coordinator -> Worker1 : Task
      | Coordinator -> Worker2 : Task
}
}

Parallel branches must be independent in order to remain well formed. Each branch operates on a disjoint set of roles. The runtime executes branches concurrently when possible.

Generated Effect Interfaces

Use the generated-interface path when the example needs typed effect boundaries or richer host/runtime integration. In these examples, the DSL remains the source of truth and Rust supplies handler implementations directly against the generated traits.

#![allow(unused)]
fn main() {
use telltale::tell;

tell! {
  type CommitError =
    | NotReady
    | TimedOut

  type alias ReadyWitness =
  {
    epoch : Int
    issuedBy : Role
  }

  type alias CommitReceipt =
  {
    commitId : String
    publishedBy : Role
  }

  effect Runtime
    authoritative ready : Session -> Result CommitError ReadyWitness
    {
      class : authoritative
      progress : may_block
      region : fragment
      agreement_use : required
      reentrancy : reject_same_fragment
    }
    command publish : ReadyWitness -> Result CommitError CommitReceipt
    {
      class : best_effort
      progress : immediate
      region : session
      agreement_use : required
      reentrancy : allow
    }

  protocol CommitFlow uses Runtime =
    roles Coordinator, Worker
    Coordinator -> Worker : Commit
}

use CommitFlow::effects;

struct Host;

impl effects::Runtime for Host {
    fn ready(&mut self, _input: effects::Session) -> Result<effects::ReadyWitness, effects::CommitError> {
        Ok(effects::ReadyWitness { epoch: 7, issued_by: effects::Role::new("Coordinator") })
    }

    fn publish(&mut self, witness: effects::ReadyWitness) -> Result<effects::CommitReceipt, effects::CommitError> {
        Ok(effects::CommitReceipt {
            commit_id: format!("commit-{}", witness.epoch),
            published_by: witness.issued_by,
        })
    }
}

let mut host = Host;
assert_eq!(CommitFlow::proof_status::STRONGEST_TIER, "session_projectable");
let ready = effects::runtime::operation("ready").unwrap();
assert!(ready.architecturally_legal());
let presence = effects::Runtime::handle(
    &mut host,
    effects::RuntimeRequest::Ready(effects::Session::new("commit-7")),
);
assert!(matches!(presence, effects::RuntimeOutcome::Ready(Ok(_))));
}

This produces canonical host-facing request/outcome enums, handler traits, and generated semantic metadata directly from the declared effect surface. Use Protocol::effects as the single import boundary. Per-interface metadata lives under Protocol::effects::<effect_name_in_snake_case>. Protocol::proof_status reports which generated proof-backed surfaces are available. File export remains tooling-only and is no longer the primary developer path.

Semantic Highlights

When you want a concrete semantic feature, start here:

  • commitment: examples/effects/commitment_lifecycle.rs
  • authority, publication, and materialization: examples/effects/generated_effect_interfaces.rs
  • structured concurrency: examples/effects/map_reduce.rs
  • profile-driven progress: examples/effects/commitment_lifecycle.rs
  • reusable domain agreement profiles: examples/effects/commitment_lifecycle.rs
  • reactive host boundaries: examples/effects/reactive_signal.rs

commitment_lifecycle.rs is the repo’s explicit example of a domain-defined agreement scheme similar in shape to Aura’s provisional / soft-safe / finalized model. It demonstrates how downstream systems can keep their own agreement vocabulary while lowering onto Telltale’s generic agreement, evidence, visibility, and progress core.

Testing Patterns

For canonical tests, exercise the generated tell! surface directly:

  • compile-time checks: assert on Protocol::proof_status
  • effect-boundary checks: implement Protocol::effects::* traits against deterministic host fixtures
  • semantic checks: assert over Protocol::commitments, Protocol::agreements, and Protocol::authority
  • protocol-machine checks: use the protocol-machine and bridge test suites for replay/parity/runtime behavior

Low-level choreography-interpreter tests still exist inside rust/runtime/tests/ as implementation coverage for that crate, but they are not the public testing model that examples should teach.

Running Examples

Run a single example with Cargo. Each example is a standalone binary that demonstrates the protocol end to end.

cargo run --example adder

This compiles and runs the adder example, which demonstrates a simple two-party request response protocol.

The wasm example crate uses its own harness. It can build the browser package, run a deterministic Node smoke check, and serve the demo locally.

cd examples/wasm
./harness.sh run

See the comments in each example file for setup requirements. For WASM-specific guidance, see WASM Guide.

WASM Guide

This guide explains how to build and run the choreography runtime on wasm32. It covers feature flags, build tooling, handler compatibility, and testing.

Overview

The telltale-runtime crate supports WASM targets. Core effects, handlers, and timeouts compile under wasm32 using wasm-bindgen-futures and wasm-timer. The same Program and interpret API used on native targets works without modification. Platform differences are handled internally by the runtime module.

What Works

In WASM builds you can use Program, interpret, and effect handlers. InMemoryHandler and TelltaleHandler are WASM compatible for local or custom transports. Middleware such as Trace, Metrics, and Retry is WASM compatible. FaultInjection is available with the test-utils feature.

Protocol definitions written with tell! produce the same projected types on both platforms when the protocol is session-projectable. The proof-status and effect surfaces are platform-agnostic. Only the lowest-level spawn and timer calls differ between native and WASM.

Limitations

WASM is single threaded. Concurrency is async only. Direct std::net sockets are not available. Network transports must use browser APIs or host provided bindings.

Tokio-specific features such as tokio::spawn are not available. Use the runtime::spawn abstraction instead. File system access is also unavailable unless the host provides a binding.

Enable WASM

Enable the wasm feature on the choreography crate.

[dependencies]
telltale-runtime = { version = "11.3.0", features = ["wasm"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"

The wasm feature enables getrandom support and pulls in the WASM runtime dependencies. Use a path dependency only for local workspace development.

Build with wasm-pack or cargo targets. wasm-pack produces a ready-to-use JavaScript package. Direct cargo build --target wasm32-unknown-unknown works for library crates that do not need JS bindings.

For reproducible local setup, install the same tool version used in CI.

cargo install wasm-pack --version 0.14.0 --locked

This installs the same wasm-pack version used in CI for reproducible builds.

wasm-pack build --target web

This produces a pkg directory with JavaScript bindings.

Minimal Example

This example runs a simple request response program using InMemoryHandler. It defines the role, label, and message types needed by the effect system. It then builds two programs and runs them concurrently with shared channels.

The first section defines the Role and Label enums. LabelId requires string round-tripping via as_str and from_str. RoleId associates a label type and provides canonical names through role_name.

#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};
use telltale_runtime::{interpret, InMemoryHandler, LabelId, Program, RoleId, RoleName};

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Role {
    Client,
    Server,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Label {
    Ok,
}

impl LabelId for Label {
    fn as_str(&self) -> &'static str {
        match self {
            Label::Ok => "ok",
        }
    }

    fn from_str(label: &str) -> Option<Self> {
        match label {
            "ok" => Some(Label::Ok),
            _ => None,
        }
    }
}

impl RoleId for Role {
    type Label = Label;

    fn role_name(&self) -> RoleName {
        match self {
            Role::Client => RoleName::from_static("Client"),
            Role::Server => RoleName::from_static("Server"),
        }
    }
}
}

The Message enum carries the payload variants exchanged between roles. Both variants use String here, but any Serialize + Deserialize type works.

#![allow(unused)]
fn main() {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
enum Message {
    Ping(String),
    Pong(String),
}
}

The canonical WASM example now lives in examples/wasm/ and uses tell! plus generated effect traits as the public surface. Browser-facing code should keep protocol structure in the DSL and host integration in the generated Protocol::effects boundary, rather than manually assembling low-level effect programs. Use ./harness.sh build to emit the browser package, ./harness.sh smoke for a deterministic Node-based end-to-end check, and ./harness.sh run to build, verify, and serve the demo locally.

For multi role tests, share channels by using InMemoryHandler::with_channels and a shared channel map. The WASM test suite in rust/runtime/tests/wasm_integration.rs shows larger examples. Each handler must reference the same Arc<Mutex<BTreeMap>> instances for messages to route correctly. The unit endpoint () is sufficient when no external state is needed.

TelltaleHandler in WASM

TelltaleHandler works in WASM with TelltaleSession pairs or custom sessions. This handler manages typed endpoints and routes messages through registered sessions. It is the recommended handler for integration-level WASM tests.

#![allow(unused)]
fn main() {
use telltale_runtime::{TelltaleEndpoint, TelltaleHandler, TelltaleSession};

let (alice_session, bob_session) = TelltaleSession::pair();
let mut alice_ep = TelltaleEndpoint::new(Role::Client);
let mut bob_ep = TelltaleEndpoint::new(Role::Server);

alice_ep.register_session(Role::Server, alice_session);
bob_ep.register_session(Role::Client, bob_session);

let mut handler = TelltaleHandler::<Role, Message>::new();
}

Use your protocol message type for Message. The role must implement both telltale::Role and RoleId. For browser transports, build a TelltaleSession from a sink and stream. Register it with TelltaleEndpoint::register_session.

Runtime Utilities

The runtime provides WASM aware task spawning helpers. These abstractions let the same protocol code run on both native and browser targets without conditional compilation at the call site.

#![allow(unused)]
fn main() {
use telltale_runtime::runtime::spawn;

spawn(async move {
    // task body
});
}

spawn uses Tokio on native targets and wasm_bindgen_futures::spawn_local on WASM.

Testing

Use wasm-bindgen-test for WASM tests. Import the crate and annotate test functions with #[wasm_bindgen_test]. Tests marked with #[wasm_bindgen_test(unsupported = test)] run as standard #[test] on native targets and as WASM tests on wasm32.

#![allow(unused)]
fn main() {
use wasm_bindgen_test::*;
}

Repository-managed tests run under Node, not a browser driver. This avoids the need for a headless browser in CI.

just wasm-test-all

For ad hoc crate-level runs, use wasm-pack test --node. This compiles the crate for wasm32 and executes tests in a Node environment.

See Choreography Effect Handlers for handler details. See Using Telltale Handlers for the channel based API.

Choreography Effect Handlers

Overview

This page documents the choreography-layer handler surface in telltale-runtime. This surface is ChoreoHandler. For protocol-machine host integration, see Effect Handlers and Session Types.

Effect interfaces are the typed operational vocabulary between the protocol machine and the world. They realize commitment conservation: every effect is a tracked commitment that must resolve to a terminal class. See Conservation Framework for the full design philosophy.

ChoreoHandler decouples protocol logic from transport implementation at the choreography layer. Handlers interpret choreographic effects into concrete communication operations. Protocol code remains unchanged across handlers. The effect runtime normalizes Parallel effects to deterministic declaration order.

Handler Domains

The effect system distinguishes two handler domains. Internal handlers are Telltale-owned and realize scheduling, dispatch, batching, replay, and simulation. External handlers are guest-runtime-facing and realize storage, network, domain checks, and other host integrations. Both domains interpret the same typed effect interfaces. Handlers may realize operational behavior, but they do not directly mutate semantic state.

Boundary Selection

Choose the handler surface by integration level.

Use caseHandler surface
Generated choreography code over typed messagesChoreoHandler
Protocol-machine bytecode execution in a host runtimeEffectHandler

EffectHandler is the integration boundary for third-party runtimes. ChoreoHandler is the integration boundary for async choreography transports.

Protocol-Machine Handler Test Path

Projects that implement EffectHandler should validate behavior in telltale-simulator. Use SimulationHarness with DirectAdapter for host handlers. Use FieldAdapter when the scenario should select the handler from built-in field parameters.

#![allow(unused)]
fn main() {
let adapter = DirectAdapter::new(&handler);
let harness = SimulationHarness::new(&adapter);
let result = harness.run(&spec)?;
}

This test path runs the same protocol-machine callback surface that production execution uses. Add assert_contracts checks to make replay and trace guarantees explicit in CI.

ChoreoHandler Trait

All handlers implement this trait.

#![allow(unused)]
fn main() {
pub trait ChoreoHandler: Send {
    type Role: RoleId;
    type Endpoint: Endpoint;

    async fn send<M: Serialize + Send + Sync>(
        &mut self, ep: &mut Self::Endpoint, to: Self::Role, msg: &M
    ) -> ChoreoResult<()>;

    async fn recv<M: DeserializeOwned + Send>(
        &mut self, ep: &mut Self::Endpoint, from: Self::Role
    ) -> ChoreoResult<M>;

    async fn choose(
        &mut self, ep: &mut Self::Endpoint, who: Self::Role,
        label: <Self::Role as RoleId>::Label
    ) -> ChoreoResult<()>;

    async fn offer(
        &mut self, ep: &mut Self::Endpoint, from: Self::Role
    ) -> ChoreoResult<<Self::Role as RoleId>::Label>;

    async fn with_timeout<F, T>(
        &mut self, ep: &mut Self::Endpoint, at: Self::Role, dur: Duration, body: F
    ) -> ChoreoResult<T>
    where
        F: Future<Output = ChoreoResult<T>> + Send;
}
}

The trait defines send, receive, choice, and timeout operations. It also provides default broadcast and parallel_send helpers.

The send method transmits a message to another role. The recv method waits for a message from another role. The choose method makes a branch selection. The offer method receives a branch selection. Nested branch, loop, timeout, and parallel interpretation returns non-duplicated receive traces.

The Endpoint associated type holds connection state. Different handlers use different endpoint types.

Send bounds and portability

The trait requires messages to be Send. The send method requires Serialize + Send + Sync. The recv method requires DeserializeOwned + Send. Handler futures require F: Future + Send in with_timeout.

This matches the requirements of target runtimes. Native targets use tokio. WASM targets use single-thread executors. The bounds keep middleware stacks interchangeable between single-threaded and multi-threaded deployments.

Code written for browsers compiles unchanged for native binaries. Work can move across threads transparently.

Built-in Handlers

InMemoryHandler

The InMemoryHandler is located in rust/runtime/src/effects/handlers/in_memory.rs. It provides fast local message passing for testing. The implementation uses futures channels internally.

Basic usage creates a handler for a single role.

#![allow(unused)]
fn main() {
use telltale_runtime::InMemoryHandler;

let mut handler = InMemoryHandler::new(Role::Alice);
}

This creates an Alice handler.

For coordinated testing between roles, use shared channels.

#![allow(unused)]
fn main() {
let channels = Arc::new(Mutex::new(BTreeMap::new()));
let choice_channels = Arc::new(Mutex::new(BTreeMap::new()));

let alice = InMemoryHandler::with_channels(Role::Alice, channels.clone(), choice_channels.clone());
let bob = InMemoryHandler::with_channels(Role::Bob, channels.clone(), choice_channels.clone());
}

The shared channels enable communication between handlers in the same process.

TelltaleHandler

The TelltaleHandler is located in rust/runtime/src/effects/handlers/telltale.rs. It provides production-ready session-typed channels. The implementation uses the core Telltale library for type-safe communication.

This handler enforces session types at runtime. It provides strong guarantees about protocol compliance.

See Using Telltale Handlers for complete documentation.

RecordingHandler

The RecordingHandler is located in rust/runtime/src/effects/handlers/recording.rs. It records all operations for verification and testing. The handler stores a log of send, recv, choose, and offer calls.

Basic usage creates a recording handler.

#![allow(unused)]
fn main() {
use telltale_runtime::RecordingHandler;

let mut handler = RecordingHandler::new(Role::Alice);
// ... execute protocol ...
let events = handler.events();
assert!(matches!(events[0], RecordedEvent::Send { from: Role::Alice, to: Role::Bob, .. }));
}

The recorded events can be inspected in tests to verify protocol behavior.

NoOpHandler

The NoOpHandler is located in rust/runtime/src/effects/handler.rs. It implements send and choose as no-ops and returns errors for recv and offer. This is useful for testing protocol structure without actual communication.

#![allow(unused)]
fn main() {
let handler = NoOpHandler::<MyRole>::new();
}

Send and choose succeed immediately without side effects. Recv and offer return transport errors.

Middleware

Middleware wraps handlers to add cross-cutting functionality. Multiple middleware can compose around a single handler.

Trace

The Trace middleware is located in rust/runtime/src/effects/middleware/trace.rs. It logs all operations for debugging. The middleware outputs send, recv, choose, and offer calls with role and message details.

Usage example shows wrapping a handler.

#![allow(unused)]
fn main() {
use telltale_runtime::Trace;

let base_handler = InMemoryHandler::new(role);
let mut handler = Trace::with_prefix(base_handler, "Alice");
}

Each operation logs before delegating to the inner handler.

Metrics

The Metrics middleware is located in rust/runtime/src/effects/middleware/metrics.rs. It counts operations for monitoring. The middleware tracks send_count, recv_count, and error_count.

Usage example shows metrics collection.

#![allow(unused)]
fn main() {
use telltale_runtime::Metrics;

let base_handler = InMemoryHandler::new(role);
let mut handler = Metrics::new(base_handler);
// ... execute protocol ...
println!("Sends: {}", handler.send_count());
}

Metrics accumulate over the handler lifetime.

Retry

The Retry middleware is located in rust/runtime/src/effects/middleware/retry.rs. It retries failed operations with exponential backoff. Only send operations are retried since recv changes protocol state.

Usage example configures retry behavior.

#![allow(unused)]
fn main() {
use telltale_runtime::Retry;
use std::time::Duration;

let base_handler = InMemoryHandler::new(role);
let mut handler = Retry::with_config(base_handler, 3, Duration::from_millis(100));
}

The handler retries up to 3 times. Delays are 100ms, 200ms, 400ms using exponential backoff.

FaultInjection

The FaultInjection middleware is located in rust/runtime/src/effects/middleware/fault_injection.rs. It requires the test-utils feature. The middleware injects random failures and delays for testing fault tolerance.

Usage example configures fault injection.

#![allow(unused)]
fn main() {
use telltale_runtime::effects::middleware::FaultInjection;
use std::time::Duration;

let base_handler = InMemoryHandler::new(role);
let mut handler = FaultInjection::new(base_handler, 0.1)
    .with_delays(Duration::from_millis(10), Duration::from_millis(100));
}

Send operations randomly fail 10% of the time. Delays range from 10ms to 100ms. Fault injection only affects send operations. The recv, choose, and offer methods delegate directly to the inner handler.

Composing Middleware

Middleware can stack in layers.

#![allow(unused)]
fn main() {
let handler = InMemoryHandler::new(role);
let handler = Retry::with_config(handler, 3, Duration::from_millis(100));
let handler = Trace::with_prefix(handler, "Alice");
let handler = Metrics::new(handler);
}

Operations flow through the stack. The order is Metrics to Trace to Retry to InMemory.

Creating Custom Handlers

Implement ChoreoHandler for your transport.

#![allow(unused)]
fn main() {
pub struct MyHandler {
    role: MyRole,
    connections: HashMap<MyRole, Connection>,
}

#[async_trait]
impl ChoreoHandler for MyHandler {
    type Role = MyRole;
    type Endpoint = MyEndpoint;
    
    async fn send<M: Serialize + Send + Sync>(
        &mut self, ep: &mut Self::Endpoint, to: Self::Role, msg: &M
    ) -> ChoreoResult<()> {
        let conn = self.connections.get_mut(&to)?;
        let bytes = bincode::serialize(msg)?;
        conn.send(bytes).await?;
        Ok(())
    }

    async fn recv<M: DeserializeOwned + Send>(
        &mut self, ep: &mut Self::Endpoint, from: Self::Role
    ) -> ChoreoResult<M> {
        let conn = self.connections.get_mut(&from)?;
        let bytes = conn.recv().await?;
        let msg = bincode::deserialize(&bytes)?;
        Ok(msg)
    }
    
    // Implement choose and offer...
}
}

The handler manages connection state and serialization. The endpoint type holds per-role state if needed.

Handler Selection Guide

Use InMemoryHandler for local testing and simple protocols.

Use TelltaleHandler for production deployments with session type guarantees.

Use RecordingHandler for test verification and debugging.

Use NoOpHandler for protocol structure testing.

Use middleware to add logging, metrics, retries, or fault injection. Middleware works with any handler.

WASM Considerations

InMemoryHandler and TelltaleHandler both work in WASM environments. They use futures channels for communication.

For WASM network communication, implement a custom handler. Use web-sys WebSocket or fetch APIs. See WASM Guide for details.

Parameterized Roles

Parameterized roles remain a choreography-level feature of the DSL and AST. Projection and interpretation still require concrete role values at execution time. Wildcard, symbolic, and unresolved range forms are not interpreted directly by ChoreoHandler. Resolve participant sets during choreography construction or initialization, then build effect programs over concrete RoleId values.

Topology constraints validate concrete deployments against role family bounds.

#![allow(unused)]
fn main() {
use telltale_runtime::topology::{Topology, parse_topology};

let config = r#"
    topology Prod for Protocol {
        role_constraints {
            Witness: min = 3, max = 10
        }
    }
"#;

let topology = parse_topology(config)?.topology;
topology.validate_family("Witness", 5)?;
}

Run this check before execution to ensure the concrete participant set matches deployment expectations.

Effect Interpretation

Handlers interpret the protocol-machine and generated effect boundary. For normal application code, start from tell!, implement the generated Protocol::effects::* traits, and let the protocol-machine/runtime drive the requests. The lower-level choreography interpreter remains an internal implementation surface.

Using Telltale Handlers

Overview

TelltaleHandler implements choreographic effects over session-typed channels. This guide documents setup patterns, API surface, and operational behavior.

The handler is the primary integration point for applications that execute protocols at runtime. It wraps session-typed channel pairs, manages endpoint lifecycle, and dispatches send and receive operations according to the choreographic projection for each participant role.

Quick Start

Basic Two-Party Protocol

use serde::{Deserialize, Serialize};
use telltale::{Message, Role};
use telltale_runtime::{
    ChoreoHandler, LabelId, RoleId, RoleName, TelltaleEndpoint, TelltaleHandler, TelltaleSession,
};

// Define roles
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum MyRole {
    Alice,
    Bob,
}

// Define a label type for choices (unused in this example)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Choice {
    Default,
}

impl LabelId for Choice {
    fn as_str(&self) -> &'static str {
        "Default"
    }

    fn from_str(label: &str) -> Option<Self> {
        match label {
            "Default" => Some(Choice::Default),
            _ => None,
        }
    }
}

impl RoleId for MyRole {
    type Label = Choice;

    fn role_name(&self) -> RoleName {
        match self {
            MyRole::Alice => RoleName::from_static("Alice"),
            MyRole::Bob => RoleName::from_static("Bob"),
        }
    }
}

impl Role for MyRole {
    type Message = MyMessage;

    fn seal(&mut self) {}

    fn is_sealed(&self) -> bool {
        false
    }
}

// Define messages
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MyMessage {
    content: String,
}

impl Message<Box<dyn std::any::Any + Send>> for MyMessage {
    fn upcast(msg: Box<dyn std::any::Any + Send>) -> Self {
        *msg.downcast::<MyMessage>().unwrap()
    }

    fn downcast(self) -> Result<Box<dyn std::any::Any + Send>, Self> {
        Ok(Box::new(self))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create endpoints
    let mut alice_ep = TelltaleEndpoint::new(MyRole::Alice);
    let mut bob_ep = TelltaleEndpoint::new(MyRole::Bob);

    // Setup sessions
    let (alice_session, bob_session) = TelltaleSession::pair();
    alice_ep.register_session(MyRole::Bob, alice_session);
    bob_ep.register_session(MyRole::Alice, bob_session);

    // Create handlers
    let mut alice_handler = TelltaleHandler::<MyRole, MyMessage>::new();
    let mut bob_handler = TelltaleHandler::<MyRole, MyMessage>::new();

    // Send and receive
    let msg = MyMessage {
        content: "Hello!".to_string(),
    };
    alice_handler.send(&mut alice_ep, MyRole::Bob, &msg).await?;

    let received: MyMessage = bob_handler.recv(&mut bob_ep, MyRole::Alice).await?;
    println!("Received: {}", received.content);

    Ok(())
}

This example creates two endpoints and connects them with a TelltaleSession pair. It demonstrates direct send and recv calls through the handler.

Simulator Regression Lane

Use telltale-simulator to regression test projects that mix TelltaleHandler style protocol logic with protocol-machine execution. The harness API gives one stable path for scenario setup and contract checks. This avoids project-specific guard boilerplate for common replay and trace assertions.

#![allow(unused)]
fn main() {
let harness = SimulationHarness::new(&DirectAdapter::new(&handler));
let result = harness.run(&spec)?;
assert_contracts(&result, &ContractCheckConfig::default())?;
}

This check complements endpoint unit tests. It validates protocol-machine behavior under the same scenario middleware used by simulator parity lanes.


Core Concepts

Roles

Roles represent participants in the choreography. They must implement:

  • telltale::Role
  • RoleId (from telltale_runtime::effects)
  • Clone, Copy, Debug, PartialEq, Eq, Hash

RoleId requires an associated label type that implements LabelId. You only need labels if your protocol uses choice.

Messages

Messages are the data exchanged between roles. They must:

  • Implement Serialize and Deserialize (via serde)
  • Implement telltale::Message
  • Be Send and Sync

Endpoints

TelltaleEndpoint<R> manages the channels and session state for a role:

  • One endpoint per role in the protocol
  • Contains channels to all peers
  • Tracks session metadata (operation counts, state descriptions)

Sessions

TelltaleSession provides the canonical communication boundary:

  • Created in pairs for in-process tests: TelltaleSession::pair()
  • Or built from custom sink/stream transports with TelltaleSession::from_sink_stream()
  • The handler serializes and deserializes messages with bincode

Handlers

TelltaleHandler<R, M> interprets choreographic effects:

  • Stateless (can be shared across operations)
  • Implements ChoreoHandler trait
  • Provides send, recv, choose, offer operations

API Reference

TelltaleEndpoint

#![allow(unused)]
fn main() {
impl<R: Role + Eq + Hash + Clone + Debug> TelltaleEndpoint<R>
}

This shows the generic bounds required by the endpoint type.

Constructor

#![allow(unused)]
fn main() {
pub fn new(local_role: R) -> Self
}

Create a new endpoint for a role.

Role Access

#![allow(unused)]
fn main() {
pub fn local_role(&self) -> &R
}

Get a reference to the local role.

Session Management

#![allow(unused)]
fn main() {
pub fn register_session(&mut self, peer: R, session: TelltaleSession)
}

Register a session (for example one produced via TelltaleSession::pair or TelltaleSession::from_sink_stream). Use this when you need additional transport logic such as WebSockets, recording, or custom middleware stacks.

#![allow(unused)]
fn main() {
pub fn has_channel(&self, peer: &R) -> bool
}

Check if a session exists for a peer.

#![allow(unused)]
fn main() {
pub fn close_channel(&mut self, peer: &R) -> bool
}

Close a specific session.

#![allow(unused)]
fn main() {
pub fn close_all_channels(&mut self) -> usize
}

Close all sessions and return count.

#![allow(unused)]
fn main() {
pub fn active_channel_count(&self) -> usize
}

Get number of active sessions.

#![allow(unused)]
fn main() {
pub fn is_all_closed(&self) -> bool
}

Check if all sessions are closed.

Metadata Access

#![allow(unused)]
fn main() {
pub fn get_metadata(&self, peer: &R) -> Option<&SessionMetadata>
}

Get session metadata for a peer.

#![allow(unused)]
fn main() {
pub fn all_metadata(&self) -> Vec<(R, &SessionMetadata)>
}

Get metadata for all sessions.

TelltaleHandler

#![allow(unused)]
fn main() {
impl<R, M> TelltaleHandler<R, M>
}

This shows the handler type parameters. The handler is generic over role and message types.

Constructor

#![allow(unused)]
fn main() {
pub fn new() -> Self
}

Create a new handler.

ChoreoHandler Implementation

#![allow(unused)]
fn main() {
async fn send<Msg>(&mut self, ep: &mut Endpoint, to: Role, msg: &Msg) -> ChoreoResult<()>
where Msg: Serialize + Send + Sync
}

Send a message to a role.

#![allow(unused)]
fn main() {
async fn recv<Msg>(&mut self, ep: &mut Endpoint, from: Role) -> ChoreoResult<Msg>
where Msg: DeserializeOwned + Send
}

Receive a message from a role.

#![allow(unused)]
fn main() {
async fn choose(&mut self, ep: &mut Endpoint, who: Role, label: Label) -> ChoreoResult<()>
}

Make a choice (internal choice).

#![allow(unused)]
fn main() {
async fn offer(&mut self, ep: &mut Endpoint, from: Role) -> ChoreoResult<Label>
}

Offer a choice (external choice).

#![allow(unused)]
fn main() {
async fn with_timeout<F, T>(&mut self, ep: &mut Endpoint, at: Role, dur: Duration, body: F) -> ChoreoResult<T>
where F: Future<Output = ChoreoResult<T>> + Send
}

Execute operation with timeout.

TelltaleSession Builders

#![allow(unused)]
fn main() {
TelltaleSession::pair()
}

Builds a connected in-process session pair.

#![allow(unused)]
fn main() {
TelltaleSession::from_sink_stream(sender, receiver)
}

Accepts any async sink and stream pair carrying Vec<u8> payloads. It exposes the pair to the handler. Use this for custom transports, then call endpoint.register_session(peer, session).

SessionMetadata

#![allow(unused)]
fn main() {
pub struct SessionMetadata {
    pub state_description: String,
    pub is_complete: bool,
    pub operation_count: usize,
}
}

This struct records session state for a peer. It is updated as operations run.

Tracks session progression:

  • state_description: Human-readable current state
  • is_complete: Whether session has completed
  • operation_count: Number of operations performed

Usage Patterns

Pattern 1: Request-Response

#![allow(unused)]
fn main() {
// Client side
let request = Request { query: "data".to_string() };
handler.send(&mut endpoint, Role::Server, &request).await?;
let response: Response = handler.recv(&mut endpoint, Role::Server).await?;
}

This pattern sends a request and waits for a response. It is the simplest round trip flow.

Pattern 2: Choice with Branches

#![allow(unused)]
fn main() {
// Sender
let decision = if condition {
    Choice::Accept
} else {
    Choice::Reject
};
handler.choose(&mut endpoint, Role::Other, decision).await?;

// Receiver
let choice = handler.offer(&mut endpoint, Role::Other).await?;
match choice {
    Choice::Accept => {
        // Handle accept branch
    }
    Choice::Reject => {
        // Handle reject branch
    }
}
}

This pattern uses choose and offer to coordinate a branch. The chosen label drives the receiver logic.

Pattern 3: Sequential Messages

#![allow(unused)]
fn main() {
for item in items {
    handler.send(&mut endpoint, Role::Peer, &item).await?;
    let ack: Ack = handler.recv(&mut endpoint, Role::Peer).await?;
}
}

This pattern sends a batch of items with acknowledgments. Each step waits for the peer response.

Pattern 4: Multi-Party Coordination

#![allow(unused)]
fn main() {
// Coordinator
let offer: Offer = handler.recv(&mut endpoint, Role::Buyer).await?;
handler.send(&mut endpoint, Role::Seller, &offer).await?;

let response: Response = handler.recv(&mut endpoint, Role::Seller).await?;
handler.send(&mut endpoint, Role::Buyer, &response).await?;
}

This pattern relays messages between two peers. It keeps the coordinator role in control of ordering.

Pattern 5: Timeout Protection

#![allow(unused)]
fn main() {
let result = tokio::time::timeout(
    Duration::from_secs(5),
    handler.recv::<Response>(&mut endpoint, Role::Server),
)
.await;

match result {
    Ok(Ok(msg)) => {
        // Process message
    }
    Ok(Err(e)) => {
        // Handle handler errors
    }
    Err(_) => {
        // Handle timeout
    }
}
}

This pattern wraps a receive in a runtime timeout. Generated effect programs use with_timeout internally when sender metadata includes runtime_timeout or when a protocol-level timeout ... on timeout ... branch is present.

Best Practices

1. Resource Management

Recommended approach:

#![allow(unused)]
fn main() {
// Close channels explicitly when done
endpoint.close_all_channels();
}

This closes channels explicitly when the protocol is complete.

Recommended alternative:

#![allow(unused)]
fn main() {
// Use Drop to ensure cleanup
{
    let mut endpoint = TelltaleEndpoint::new(role);
    // ... use endpoint ...
} // Drop ensures cleanup
}

This relies on drop to clean up resources at scope end.

Avoid:

#![allow(unused)]
fn main() {
// Don't forget to clean up resources
let mut endpoint = TelltaleEndpoint::new(role);
// ... use endpoint ...
// Forgot to close!
}

This leaves channels open after the protocol.

2. Error Handling

Recommended approach:

#![allow(unused)]
fn main() {
match handler.send(&mut ep, role, &msg).await {
    Ok(()) => { /* success */ }
    Err(ChoreographyError::Transport(e)) => {
        // Handle transport error
        tracing::error!("Send failed: {}", e);
    }
    Err(e) => {
        // Handle other errors
    }
}
}

This handles transport errors explicitly. It keeps other errors visible.

Avoid:

#![allow(unused)]
fn main() {
// Don't ignore errors
handler.send(&mut ep, role, &msg).await.unwrap();
}

This panics on failures and hides transport details.

3. Session Setup

Recommended approach:

#![allow(unused)]
fn main() {
// Setup all sessions before starting protocol
let (alice_session, bob_session) = TelltaleSession::pair();
alice_ep.register_session(Role::Bob, alice_session);
bob_ep.register_session(Role::Alice, bob_session);

// Then start protocol
protocol_run().await?;
}

This ensures sessions exist before the first send.

Avoid:

#![allow(unused)]
fn main() {
// Don't register sessions mid-protocol
handler.send(&mut ep, role, &msg).await?; // Might not have session!
ep.register_session(role, session); // Too late!
}

This can cause send failures when a session is missing.

4. Metadata Usage

Recommended approach:

#![allow(unused)]
fn main() {
// Use metadata for debugging and monitoring
if let Some(meta) = endpoint.get_metadata(&peer) {
    tracing::info!(
        peer = ?peer,
        operations = meta.operation_count,
        state = %meta.state_description,
        "Session status"
    );
}
}

This reports progress and state for each peer. It is useful for debugging.

5. Testing

Recommended approach:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_protocol() {
    // Setup test environment
    let mut alice_ep = TelltaleEndpoint::new(Role::Alice);
    let mut bob_ep = TelltaleEndpoint::new(Role::Bob);
    
    let (alice_session, bob_session) = TelltaleSession::pair();
    alice_ep.register_session(Role::Bob, alice_session);
    bob_ep.register_session(Role::Alice, bob_session);
    
    // Test protocol
    let msg = TestMessage { data: vec![1, 2, 3] };
    handler.send(&mut alice_ep, Role::Bob, &msg).await.unwrap();
    
    let received: TestMessage = handler.recv(&mut bob_ep, Role::Alice).await.unwrap();
    assert_eq!(received.data, vec![1, 2, 3]);
}
}

This sets up a local session pair and exercises a full send and receive. It validates handler wiring in tests.

Operational Notes

TelltaleHandler is intentionally thin. Most correctness comes from the session-typed state in TelltaleEndpoint and from protocol generation. Treat the handler as a transport adapter with stable behavior. Keep role definitions and message definitions close to each protocol module to make type drift easy to detect during review.

For production integrations, prefer explicit lifecycle management even though drop-based cleanup exists. Close sessions when a protocol is complete. Inspect endpoint metadata after important milestones such as branch commits or retries. Metadata captures operation counts and completion status per peer.

When introducing custom transports, keep serialization and framing deterministic. Use one canonical message encoding in both tests and production. Add at least one integration test that runs both endpoints concurrently and one scenario test that exercises failure handling.

Effect Handlers and Session Types

This document defines the integration boundary between Telltale and a host runtime. The boundary is the protocol-machine EffectHandler interface. It is centered on typed EffectRequest and EffectOutcome values.

Scope

Use this document when integrating Telltale into another execution environment. Use Choreography Effect Handlers when implementing async handlers for generated choreography code.

This bridge is for protocol-critical capability and evidence flow. It is not a general host authorization framework. Arbitrary application auth, policy, and resource-permission systems remain outside this boundary.

Three-Layer Contract

Telltale uses three layers.

LayerArtifactRole
Global protocol layerchoreography and projectiondefines role-local protocol obligations
Effect layerhandler interfacesdefines runtime action behavior
Protocol-machine layerbytecode and monitor checksenforces instruction-level typing and admission rules

Projection and runtime checks preserve obligations across these layers.

This document describes a host-runtime contract. It is normative for Rust embedders. It is not itself a theorem statement. The theorem-backed protocol properties remain in projection, coherence, and harmony. The host ownership rules below are implementation contracts enforced by the protocol-machine boundary.

Rust Handler Surfaces

Rust exposes two handler interfaces.

InterfaceLocationPurpose
ChoreoHandlerrust/runtime/src/effects/handler.rsasync typed API for generated choreography code
EffectHandlerrust/machine/src/effect.rssync protocol-machine API over bytecode values

Third-party runtime integration should use EffectHandler.

Why the Protocol-Machine Boundary

EffectHandler is synchronous. This matches deterministic host execution models. It avoids futures and runtime-specific scheduling inside handler calls.

EffectHandler operates on protocol-machine values and labels. This keeps the wire and storage boundary host-defined. It avoids coupling to generated Rust message types.

The protocol machine enforces session typing before and during execution. The boundary keeps typing logic in Telltale. It keeps host logic focused on effect interpretation.

Canonical Ingress and Ownership

External host events must enter the protocol machine through canonical typed effect requests.

Typed ingressPurposeOwnership rule
EffectRequestBody::TopologyEventsinject crash, partition, heal, corruption, and timeout eventsmust not mutate session-local host state directly
EffectRequestBody::SendDecisioncompute outbound delivery behaviormay inspect request-local state only
EffectRequestBody::Receiveapply receive-side host effectsmay mutate request-local state only
EffectRequestBody::InvokeStepperform Invoke-scoped integration workmay mutate request-local state only
EffectRequestBody::OutputConditionHintprovide authoritative commit metadata for proof-bearing success and canonical handle issuancemust not be synthesized from observational snapshots

Session-local host mutation outside these request-local values flows through an explicit ownership capability such as OwnedSession. This is the host integration path for mutating edge traces, handler bindings, or other session-local host metadata.

Protocol-critical host decisions should also use explicit witnesses where available:

  • readiness checks should issue and later consume a ReadinessWitness
  • ownership-failure cancellation paths issue a CancellationWitness
  • topology timeout ingress emits a TimeoutWitness

Host rules:

  • EffectHandler methods are synchronous. Async work must happen outside request handling and feed results back through a later ingress call.
  • Admission and ownership are distinct. Passing admission/runtime gates does not imply the caller is the current session owner.
  • Ownership is generation-bearing. Reusing the same owner label after transfer does not preserve authority.
  • Fragment-scoped capabilities do not imply full-session ingress rights.
  • Stale owners fail closed with typed ownership diagnostics before host mutation is applied.

Boundary Ownership

The boundary separates protocol semantics from host materialization.

ConcernProtocol machine ownsHost EffectHandler owns
Session typingLocal type checks and continuation advancementnone
Buffer and signature disciplineenqueue, dequeue, and signature verificationnone
Scheduler and commitcoroutine scheduling and atomic step commitnone
Send policycall point and branch label contextsend_decision return value
Receive side effectsreceive timing and source payloadregister and host state mutation in handle_recv
Invoke integrationwhen invoke runsintegration state mutation in step
Guard transitionsprotocol-machine guard instruction flowgrant or block in handle_acquire, validation in handle_release
Topology metadataevent ingestion order and applicationproduced events in topology_events
Output metadatacommit-time query pointoptional hint from output_condition_hint

Additional ownership split:

ConcernProtocol machine ownsHost runtime owns
current owner identity and generationvalidation and stale-owner rejectionchoosing owner labels and transfer policy
transfer receipts and rollbackstaged transfer enforcement and audit artifactswhen to request transfer
session-local mutation gatecapability and scope checksoperations performed through OwnedSession

Typed Effect Boundary

The protocol-machine dispatch path is in rust/machine/src/engine/. The trait surface is in rust/machine/src/effect.rs. The normative contract is documented in that trait module.

Surfaceprotocol-machine call pointRuntime behaviorIntegration note
handle_effect(EffectRequest)all host-facing instruction sitesone canonical request/outcome surfacenew runtime code must use this path
EffectRequest.metadataall request construction sitescarries EffectInterfaceMetadata fields: interface name, operation name, authority class, admissibility, totality, timeout, reentrancy, handler domainvalidated before dispatch
EffectRequestBody::SendDecisionstep_send, step_offercalled before enqueuereceives send context plus optional precomputed payload
EffectRequestBody::Receivestep_recv, step_choosecalled after dequeue and verificationuse for state updates and host-side effects
EffectRequestBody::Choosetrait helper / custom runnersbranch-selection helpernot part of the canonical default dispatch path
EffectRequestBody::InvokeStepstep_invokecalled during Invoke instructionuse for integration steps and persistent deltas
EffectRequestBody::Acquirestep_acquiregrant, block, or fail acquirereturn evidence in EffectResponse::Acquire
EffectRequestBody::Releasestep_releaserelease validationreturn EffectOutcome::failure(...) to reject invalid evidence
EffectRequestBody::TopologyEventsingest_topology_eventscalled once per scheduler tickevents are sorted by deterministic ordering key before apply
EffectRequestBody::OutputConditionHintpost-dispatch pre-commitqueried only when a step emits eventsreturn None to use protocol-machine default
handler_identitytrace and commit attributionstable handler id in tracesdefaults to DEFAULT_HANDLER_ID when not overridden

Helper-method compatibility notes:

  • handle_send and handle_choose must not become hidden side channels for session metadata mutation.
  • helper methods remain compatibility helpers for default handle_effect implementations. They are not separate ingress paths.
  • Bridge traits in rust/machine/src/bridge.rs are deterministic lookup/projection surfaces, not mutation surfaces.
  • Public host integrations open sessions through load_choreography_owned(...) and mutate session-local host metadata through OwnedSession.

Typed Effect Outcomes

The protocol machine now uses a typed effect boundary. EffectHandler::handle_effect returns an EffectOutcome. Helper methods use typed EffectResult<T> rather than Result<T, String>.

Outcome surfacePurpose
EffectOutcome::success(EffectResponse)request completed successfully
EffectOutcome::blocked() / EffectResult::Blockedrequest requested a clean scheduler-visible block
EffectOutcome::failure(EffectFailure) / EffectResult::Failure(EffectFailure)request failed with typed diagnostics
EffectFailureKindcoarse failure taxonomy including denied, timeout, cancellation, stale authority, invalid evidence, unavailable, invalid input, determinism, topology disruption, and contract violation

Host guidance:

  • Use Blocked only for genuinely resumable conditions such as acquire deferral.
  • Use Failure for typed terminal or policy-visible failures.
  • Do not encode timeout, cancellation, or ownership failure in ad hoc strings when a specific EffectFailureKind exists.
  • Replay, recording, bridge export, and effect-exchange serialization preserve these typed outcomes directly.

Language-Declared Effect Invocation

The choreography language now has nominal effect declarations, protocol-level uses clauses, and check Effect.op(...) expressions.

effect Runtime
  authoritative ready : Session -> Result CommitError ReadyWitness
  {
    class : authoritative
    progress : may_block
    region : fragment
    agreement_use : required
    reentrancy : reject_same_fragment
  }

protocol CommitFlow uses Runtime =
  roles Coordinator, Worker
  authoritative let readiness = check Runtime.ready(session)
  Coordinator -> Worker : Commit

Host-boundary rule:

  • this does not create a second integration channel beside the protocol-machine effect layer
  • language-declared effect operations still lower to the same typed protocol machine invoke boundary and handler-obligation model
  • missing or ambiguous authority must surface as typed failure or explicit evidence rejection, never as fallback success

Operational consequence:

  • embedders implement one typed host boundary in EffectHandler
  • language-level authority checks become explicit effect observations and semantic-audit records once lowered into the protocol machine

Authoritative Reads, Materialization, and Publication

The protocol machine now distinguishes observational reads from semantic-path authoritative reads.

SurfaceMeaning
ObservedReadhandler/effect observation only. Must not authorize semantic truth.
AuthoritativeReadwitness-bearing semantic input accepted on parity-critical paths
MaterializationProofproof-bearing success artifact derived from canonical output-condition checks
CanonicalHandlestrong runtime handle that later parity-critical paths must require
PublicationEventone sealed canonical publication path for lifecycle-visible semantic state

Host-runtime rules:

  • observational effect results must not be promoted into semantic truth
  • proof-bearing success is mandatory when an operation declares requires_proof
  • canonical publication is derived from sanctioned runtime state. Embedders must not bypass it with parallel publish helpers.
  • parity-critical follow-on work should require a CanonicalHandle, not a weak id reconstructed from observational state

Authority Witnesses

The ownership boundary now includes explicit witness objects for protocol-critical authority flow.

WitnessIssued byConsumed byPurpose
ReadinessWitnessSessionStore::issue_readiness_witness or OwnedSession::issue_readiness_witnessconsume_readiness_witnessprove a readiness/admission-style check under a specific live owner generation
CancellationWitnessowner death or abandoned-transfer handlingobservational/audit surfacemake cancellation-triggering ownership failure explicit
TimeoutWitnesstopology timeout ingressobservational/audit surfacemake timeout issuance explicit and replay-visible

Readiness witnesses are generation-bound and single-use. They fail closed if the owner becomes stale, scope attenuates, the witness is forged, or a second consume is attempted. These runtime witnesses are the evidence objects that back language-level authority checks and evidence-bearing branches after lowering.

Integration Workflow

  1. Use telltale-theory at setup time to project global types to local types.
  2. Compile local types to protocol-machine bytecode and load with CodeImage.
  3. Open sessions with load_choreography_owned(...) so the host authority boundary is explicit from the first step.
  4. Implement EffectHandler with deterministic host operations.
  5. Map each typed effect request to host primitives without reimplementing protocol typing.
  6. Run run_loaded_protocol_machine_record_replay_conformance to validate record and replay behavior on a loaded protocol machine.
  7. Run Lean bridge lanes for parity and equivalence checks.

Integration Tooling

When repository tooling needs exported files on disk, use just effect-scaffold to generate host integration stubs. This is not the normal application path. Normal code should implement the typed effect interfaces emitted directly by tell!.

The command reads DSL effect declarations and emits:

  • generated_effects.rs with canonical Rust request/outcome enums and host-handler traits
  • generated_simulator.rs with first-class simulator traits, state, and scenario builders
  • generated_effect_manifest.json with the exported effect-family schema
  • a local generated README.md with next-step instructions
just effect-scaffold path/to/protocol.tell

This command writes files under artifacts/effect_handler_scaffold by default. The direct form is:

cargo run -p telltale-runtime --bin effect-scaffold -- --out artifacts/effect_handler_scaffold --dsl path/to/protocol.tell

Use --no-simulator when you want only the Rust effect boundary without simulator artifacts.

Use just sim-run <config> to execute a simulator harness config file. This command runs the protocol machine with scenario middleware and contract checks. It is the fastest path for CI validation in third party host projects.

just sim-run artifacts/sim_integration.toml

This command prints a JSON report. The process exits with code 2 when contract checks fail.

Simulator Validation Lane

Use telltale-simulator harness types for integration tests. HarnessSpec carries local types, global type, scenario, and optional initial states. SimulationHarness runs the spec with one HostAdapter.

#![allow(unused)]
fn main() {
let adapter = DirectAdapter::new(&my_effect_handler);
let harness = SimulationHarness::new(&adapter);
let result = harness.run(&spec)?;
assert_contracts(&result, &ContractCheckConfig::default())?;
}

This lane validates runtime behavior without reimplementing protocol-machine checks in the host project. See Simulation Overview for harness config fields and preset helpers.

Performance and Diagnostics Controls

ProtocolMachineConfig.effect_trace_capture_mode controls effect trace overhead. Default mode is full.

ProtocolMachineConfig.payload_validation_mode controls runtime payload hardening. Use off for trusted benchmarks, structural for standard deployments, and strict_schema for adversarial inputs.

ProtocolMachineConfig.max_payload_bytes bounds payload size in protocol-machine message validation. Default is 65536.

ProtocolMachineConfig.host_contract_assertions enables runtime checks for handler identity stability, topology ordering inputs, and transfer-event auditability. Default value is false.

Integration Checklist

  • Determinism: use stable ordering and deterministic serialization.
  • Atomicity: ensure a failed effect does not leave partial host state.
  • Isolation: keep handler state scoped to the active endpoint and session.
  • Ownership: route session-local host mutation through a current ownership capability, not ad hoc session-store access.
  • Canonical ingress: surface async work by later ingress calls rather than performing it inside synchronous request handling.
  • Replayability: validate traces with RecordingEffectHandler and ReplayEffectHandler.
  • Admission: keep protocol-machine runtime gates and profile settings explicit in ProtocolMachineConfig.

Lean Correspondence

Lean splits effect execution and typing. This split is in lean/Runtime/ProtocolMachine/Model/TypeClasses.lean and the typed request/outcome model in lean/Runtime/ProtocolMachine/Model/Effects.lean.

Rust or protocol-machine surfaceLean surfacePurpose
EffectHandler execution boundaryEffectRuntime.execexecutable effect semantics
handler typing obligationEffectSpec.handlerTypetyping-level effect contract
typed request/outcome modelRuntime/ProtocolMachine/Model/Effects.leanshared effect-interface metadata plus request/outcome correspondence
invoke typingWellTypedInstr.wt_invoke in lean/Runtime/ProtocolMachine/Runtime/Monitor.leanties invoke to handler type
behavioral equivalenceRuntime/Proofs/EffectBisim/*observer-level bisimulation bridge
config equivalence bridgeRuntime/Proofs/EffectBisim/ConfigEquivBridge.leanlinks protocol quotient and effect bisimulation
composed effect domainsRuntime/Proofs/ProtocolMachine/DomainComposition.leansum and product composition instances

Glossary

TermMeaning
Program and Effectchoreography free algebra in telltale-runtime
ChoreoHandlerasync typed handler for generated choreography code
EffectHandlersync protocol-machine host interface for runtime integration
EffectRuntimeLean executable effect action and context transition
EffectSpecLean typing obligation for effect actions
telltale_types::effectsshared deterministic clock and RNG traits for simulation support

Protocol Machine Architecture

This document defines the protocol-machine architecture, scheduling semantics, and concurrency envelope. These surfaces are used by Rust runtime targets and Lean conformance surfaces.

Transport security boundary: the runtime topology TCP helper is trusted-network-only. The reference telltale-transport crate supports pre-shared-key peer authentication and can export a semantic transport contract for theorem-pack admission, but it is still plaintext TCP. Use trusted-network TCP only on localhost, a trusted host network, or a trusted VPN unless a deployment provides an authenticated transport profile that satisfies the protocol machine’s documented transport contract.

Architecture Overview

The protocol machine is the sole authority over semantic progression. It realizes structure conservation and authority conservation from the Conservation Framework. All protocol-visible truth is committed through the protocol machine. Handlers may stage and return effect outcomes, but they do not mutate semantic state directly.

The canonical semantic authority is ProtocolMachineKernel. The cooperative ProtocolMachine and threaded ThreadedProtocolMachine (backed by NativeThreadedDriver) are the guest-runtime execution surfaces that call kernel-owned step entrypoints. Both implement the KernelMachine trait, which provides kernel_step_round for executing scheduler rounds.

The runtime keeps a single state model across targets. Core state includes coroutines, sessions, scheduler queues, observable trace, and effect trace. It also includes live operation-instance state, live outstanding-effect state, delegation audit records, and failure-topology snapshot fields. The canonical exported semantic surface is the semantic-object family: OperationInstance, OutstandingEffect, SemanticHandoff, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, PublicationEvent, ProgressContract, and ProgressTransition.

Canonicalization is not implicit in those object lists. The runtime derives a first-class finalization subsystem from them in rust/machine/src/semantic_objects.rs:

  • ProtocolMachineFinalization
  • FinalizationPath
  • FinalizationReadClass
  • FinalizationStage

That subsystem is the explicit runtime boundary between provisional observation, authoritative evidence, proof-bearing materialization, canonical publication/handle issuance, invalidation after handoff, and rejected publication paths.

The first-class protocol-critical capability boundary is intentionally split into four classes:

  • admission capability surfaces
  • ownership capability surfaces
  • evidence/finalization capability surfaces
  • transition capability surfaces

The source-of-truth Rust and Lean boundaries for that taxonomy are rust/machine/src/capabilities.rs and lean/Runtime/Proofs/Capabilities.lean. The bridge-facing strict correspondence surface for the first-class capability/finalization model is rust/bridge/tests/capability_model_correspondence.rs against inspectCapabilityModel in the Lean runner.

The canonical round model is one semantic step when concurrency is nonzero. Threaded execution is admitted as an extension only when the wave certificate gate is satisfied.

Engine Roles

EngineRoleContract Surface
ProtocolMachine (protocol machine)Canonical cooperative protocol machineExact reference for parity at concurrency 1
ThreadedGuestRuntime (NativeThreadedDriver)Parallel wave executorCertified-wave execution with fallback to canonical one-step
WASM guest runtimeSingle-thread deploymentCooperative schedule only

Scheduler Semantics

Canonical scheduling is one semantic step when concurrency is nonzero. ProtocolMachineKernel owns the selection and step contract. For cooperative execution, this gives exact behavior for deterministic replay and baseline parity.

The canonical Lean runner is runScheduled in Runtime/ProtocolMachine/Runtime/Runner.lean. For nonzero concurrency, canonical round semantics normalize to one scheduler step. This model is the semantic reference for parity at concurrency 1.

Scheduler Policies

Scheduler policy controls selection order. Policy does not change instruction semantics.

PolicyPrimary Runtime Use
CooperativeCanonical single-step execution and WASM deployment
RoundRobinFair queue progression in native runs
PriorityExplicit priority maps or token weighting
ProgressAwareToken-biased pick behavior

Threaded Wave Execution

Threaded execution selects compatible picks per wave. Compatibility is lane-disjoint, session-disjoint, and footprint-disjoint for picks in the same wave.

The threaded extension is defined in Runtime/ProtocolMachine/Runtime/ThreadedRunner.lean. Concurrency n = 1 is theorem-equal to canonical execution. For n > 1, execution is admitted through certified waves. Invalid certificates degrade to canonical one-step behavior.

Each wave is checked against a WaveCertificate. If certificate validation fails, the runtime degrades to canonical one-step behavior for that round.

Refinement Envelope

Concurrency RegimeRequired ContractEnforcement Lane
n = 1Exact canonical paritythreaded_equivalence.rs
n > 1Envelope-bounded parity with declared EnvelopeDiffparity_fixtures_v2.rs
Invalid wave certificateMandatory fallback to canonical one-stepthreaded_lane_runtime.rs
Undocumented deviationActive deviation coverage requiredjust check-parity --types

The regression lane validates both regimes. The Rust test canonical_parity_is_exact_at_concurrency_one in rust/machine/tests/parity_fixtures_v2.rs enforces exact equality. The Rust test envelope_bounded_parity_holds_for_n_gt_1 in the same file enforces envelope-bounded behavior. These are executable regression tests, not Lean theorems.

Runtime Gates

Runtime mode admission and profile selection are capability gated.

GateRuntime SurfaceCurrent Rust Path
Advanced mode admissionrequires_protocol_machine_runtime_contracts and admit_protocol_machine_runtimerust/machine/src/runtime_contracts.rs, rust/machine/src/composition.rs
Envelope execution-profile admissionexecution_profile_supported against a Lean-produced LeanEvidenceInventory (default: not admitted)rust/machine/src/runtime_contracts.rs, rust/machine/src/composition.rs
Determinism profile validationrequest_determinism_profilerust/machine/src/runtime_contracts.rs, rust/machine/src/composition.rs
Threaded certified-wave fallbackWaveCertificate check with one-step degraderust/machine/src/threaded.rs
Deviation registry enforcementUndocumented parity drift rejectionjust check-parity --types

Runtime Hardening Contracts

The guest-runtime adapters now enforce explicit runtime hardening at load and startup boundaries.

  • ThreadedProtocolMachine (backed by NativeThreadedDriver) provides with_workers for initialization. The inner driver also provides try_with_workers for fallible initialization.
  • Cooperative and threaded load_choreography paths validate trusted CodeImage runtime shape before session allocation.
  • Preferred host integration uses load_choreography_owned(...) and OwnedSession when the embedding runtime needs explicit session ownership after open.
  • Register-bound violations are fail-closed through Fault::OutOfRegisters rather than unchecked index panic in executable instruction paths.
  • Language-level effect declarations and check expressions lower through the same Invoke/EffectHandler boundary rather than introducing a second host execution channel.

Host Ownership Contract in the Runtime

The protocol-machine architecture now distinguishes three runtime concepts:

Runtime conceptPurpose
protocol typingmonitor/local-type correctness
capability admissionwhether a runtime mode/profile is allowed
current ownershipwhich host capability may mutate session-local runtime state right now

Ownership is a host-runtime contract, not a replacement for typing or admission.

Runtime ownership details:

  • session-local host mutation flows through an ownership capability carrying owner label, generation, and scope
  • transfer is staged with explicit receipts
  • readiness, cancellation, and timeout witnesses are first-class runtime objects with explicit lifecycle transitions
  • delegation emits auditable transfer records
  • host assertion mode can reject transfer events that do not have matching committed audit records
  • language-level authority/evidence constructs must lower to these runtime ownership and audit surfaces instead of bypassing them with host-local heuristics

Failure and Timeout Event Surface

Failure-visible behavior now uses explicit observable events rather than relying on host-side inference from final state alone.

EventRuntime role
TimeoutIssuedrecords deterministic timeout activation and timeout-witness issuance
CancellationRequestedrecords the start of an explicit cancellation path
Cancelledrecords successful cancellation completion
FailureBranchEnteredrecords typed failure visibility before coroutine fault finalization
SessionTerminalrecords the terminal reason for close, cancel, abort, or fault

For canonical ordering, the cooperative runtime emits the explicit event sequence before any coarser terminal summary. Threaded execution must preserve the same per-session ordering in its observable trace envelope.

Semantic Audit Surface

Replay-visible auditability now has one canonical surface derived from existing semantic artifacts rather than ad hoc logging-only streams. Effect observations now flow from the runtime’s live outstanding-effect state rather than being reconstructed only from generic trace order.

Source surfaceCanonical semantic record
authority witness audit logSemanticAuditRecord::Authority
delegation audit logSemanticAuditRecord::Delegation
explicit failure/timeout/cancellation/session-terminal eventsFailureBranchEntered, TimeoutIssued, CancellationRequested, Cancelled, SessionTerminal
outstanding-effect runtime stateEffectObservation with nominal interface/operation classification

Runtime accessors:

  • ProtocolMachine::semantic_audit_log()
  • ThreadedGuestRuntime::semantic_audit_log()
  • canonical_replay_fragment().semantic_audit_log
  • ProtocolMachine::capability_lifecycle_audit_log()
  • ThreadedGuestRuntime::capability_lifecycle_audit_log()

This keeps replay, simulator harnesses, and parity checks aligned on one derived semantic audit vocabulary. Language-level authority checks are expected to arrive at this same audit surface after lowering, with the nominal effect interface and operation name retained in EffectObservation.

Canonical Semantic Objects

The protocol machine now exports one higher-level semantic object bundle derived from live operation/effect runtime state plus authority, delegation, and output-condition surfaces.

Runtime accessors:

  • ProtocolMachine::semantic_objects()
  • ThreadedGuestRuntime::semantic_objects()
  • canonical_replay_fragment().semantic_objects

This bundle is the canonical bridge-facing and replay-facing representation of operation instances, outstanding effects, semantic handoffs, authoritative and observed reads, materialization proofs, canonical handles, publication events, progress contracts, and progress transitions.

OperationInstance and OutstandingEffect are first-class runtime objects. They carry owner id, budget ticks, retry policy, invalidation token, dependent-operation edges, and terminal publication state. Replay export and bridge payloads consume those runtime objects directly instead of deriving them later from raw trace order.

ProgressContract is now live runtime state, not a post-hoc summary. It carries bounded-wait metadata, last-progress tick, escalation timestamp, and the current explicit state (pending, blocked, no_progress, degraded, timed_out, and the existing terminal states). ProgressTransition makes each escalation replay-visible. This allows late-result invalidation and degraded behavior to be compared across cooperative, threaded, and wasm targets.

PublicationEvent is the one canonical runtime publication surface. It carries publication id, operation id, observer class, publication status, and optional proof/handle references. Proof-bearing success and publication-path uniqueness are replay-visible through this surface.

Delegation and Reconfiguration Path

Runtime delegation uses one sanctioned manager-style path rather than scattered owner mutation.

StepRuntime behavior
decode transferextract endpoint and target coroutine
coherence validationensure source, target, and delegated endpoint stay within the intended session boundary
issue receiptallocate an explicit delegation receipt with endpoint-scoped authority
apply transfermove endpoint bundle and associated runtime state
post-checkvalidate resulting owner state
audit or rollbackrecord committed transfer or roll back and emit rollback audit

This path is the runtime realization of delegation/reconfiguration. It should not be read as the theorem statement itself. The theorem-level side remains DelegationWF and related harmony results.

Reconfiguration is no longer only a single-step membership swap. The runtime now supports deterministic multi-step reconfiguration plans with:

  • canonical per-phase ReconfigurationEvent artifacts
  • placement observations and derived transport-boundary summaries per phase
  • atomic plan execution, so invalid later steps do not partially commit earlier transitions
  • serializable reconfiguration snapshots that preserve membership history and executed plan artifacts across recovery

Typesafe runtime upgrade is now modeled as a specialized transition family inside that same reconfiguration subsystem rather than as an ambient host story. The canonical upgrade surfaces are:

  • RuntimeUpgradeRequest
  • RuntimeUpgradeExecution
  • RuntimeUpgradeArtifact
  • TransitionArtifactPhase

Each runtime upgrade records an explicit staged, admitted, committed-cutover, rolled-back, or failed artifact sequence. The admitted cutover contract is explicit about:

  • execution-profile compatibility
  • ownership continuity across the first cutover
  • pending-effect treatment
  • canonical publication continuity

Those upgrade artifacts are serialized inside ReconfigurationRuntimeSnapshot.runtime_upgrades, so replay, recovery, and bridge-facing export can distinguish committed cutover from rollback/failure instead of inferring it from final membership alone.

These artifacts stay transport agnostic. They record resolved placement facts and boundary classes (in_process, shared_memory, network) rather than any deployment-product-specific backend details.

Capability Gate Architecture

Capability GateLean SurfaceRust Surface
Advanced mode admissionrequiresVMRuntimeContracts, admitVMRuntimerequires_protocol_machine_runtime_contracts, admit_protocol_machine_runtime
Live migration switchrequestLiveMigrationRuntime contracts capability booleans in composition admission
Autoscale/repartition switchrequestAutoscaleOrRepartitionRuntime contracts capability booleans in composition admission
Placement refinement switchrequestPlacementRefinementRuntime contracts capability booleans in composition admission
Relaxed reordering switchrequestRelaxedReorderingRuntime contracts capability booleans in composition admission

Determinism Profiles

ProtocolMachineConfig.determinism_mode includes Full, ModuloEffects, ModuloCommutativity, and Replay.

ProfileLean ProfileRust ProfileGate Requirement
FullfullDeterminismMode::FullProfile artifact support
Modulo effect tracemoduloEffectTraceDeterminismMode::ModuloEffectsMixed-profile capability plus artifact support
Modulo commutativitymoduloCommutativityDeterminismMode::ModuloCommutativityMixed-profile capability plus artifact support
ReplayreplayDeterminismMode::ReplayMixed-profile capability plus artifact support

Communication Consumption Identity

Communication replay-consumption uses one canonical identity schema across send and receive checks.

Canonical identity fields:

  • Domain tag: telltale.comm.identity.v1
  • Session id: sid
  • Directed edge endpoints: sender, receiver
  • Protocol step context: step_kind (send, recv, offer, choose) and local label context
  • Message label: label
  • Payload digest: domain-separated digest of serialized payload bytes
  • Sequence number: seq_no (used by sequence mode, carried for all modes)

Replay semantics by mode:

  • off: no replay-consumption checks are enforced.
  • sequence: receive must consume exactly the expected next seq_no per (sid, sender, receiver).
  • nullifier: receive computes a nullifier over the canonical identity and rejects already-consumed identities.

Replay outcomes:

  • Duplicate delivery: reject in sequence and nullifier, accept in off.
  • Reordered delivery: reject in sequence, accepted when unseen in nullifier, accept in off.
  • Cross-session reuse: reject in sequence and nullifier because sid is part of canonical identity.

Proved Theorem Surfaces

AreaLean SurfaceStatus
Canonical round normalizationRuntime/Proofs/Concurrency.leanProved
Threaded equality at n = 1sched_round_threaded_one_eq_sched_round_one, run_scheduled_threaded_one_eq_run_scheduledProved
Per-session trace equality at n = 1per_session_trace_threaded_one_eq_canonicalProved
Scheduler theorem exportsRuntime/Proofs/protocol machine/Scheduler.leanProved

Premise-Scoped Interface Surfaces

AreaLean SurfaceInterface Type
Threaded n > 1 refinementThreadedRoundRefinementPremisesPremise-scoped
Runtime admission/profile gatesRuntime/Proofs/Contracts/RuntimeContracts.leanInterface consumed by runtime
Theorem-pack capability inventory APIsRuntime/Proofs/TheoremPack/API.leanInterface consumed by runtime

These interfaces are intentionally explicit. They are not claimed as unconditional global theorems. Canonical one-step normalization and n = 1 refinement are theorem-backed in Lean. Higher-concurrency threaded refinement is modeled as a certified interface with premise-scoped refinement obligations.

Rust uses executable certificate checking and parity fixtures as release guards.

Release and CI Conformance

Release conformance surfaces are exported through theorem-pack APIs and enforced by just check-release-conformance. Parity and drift governance are enforced by just check-parity --all.

Targeted Runtime Retirement

The exclusive machine owner can acknowledge targeted close and reap on either cooperative or threaded backends. Successful retirement removes the target’s live execution and scheduler custody while preserving unrelated sessions and stable coroutine identifiers. Threaded acknowledgment follows completion of the owned worker scope. Required preflight faults return typed lifecycle errors before mutation. Session Lifecycle defines the terminal epoch, archival, and acknowledgment contracts.

Live Effect Custody and History Retention

The existing outstanding-effect, operation-instance and progress-contract fields are canonical resident state used by reentrancy and bounded progress enforcement. They retain all live records independently of optional diagnostic retention on both backends. Disabled retention removes retired terminal history, and Capped retention keeps at most its configured number of terminal records per stream while preserving live obligations and all records of resident sessions. Oldest eligible retired terminal records are pruned first. A live obligation is never evicted merely because a later topology exchange or other observation was recorded. Total resident records can exceed the history cap. That cap is not semantic admission control.

Actual completion, timeout/invalidation, targeted session disposal and ownership handoff update the same original fields before terminal history pruning. These are protocol-machine lifecycle transitions. They do not acknowledge teardown of an arbitrary host future or native worker, and audit serialization cannot reconstruct live authority. The shared actual-producer tests exercise owned Acquire, subsequent topology ingress, reentrancy/progress and targeted settlement with Capped1 and Disabled. Declarations alone are not execution evidence.

Terminal status alone does not make a record diagnostic: the actual commit path uses resident operation/publication records to derive the next WalSyncRequest. All three canonical streams therefore also retain every record associated with an actually resident session, including terminal effects and operations. Only after the original session store acknowledges its removal can terminal records become history eligible for pruning. Ordinary closed-but-resident sessions keep that evidence. This retains the original records, not a reconstructed summary or parallel WAL ledger. A busy/poisoned threaded registry cannot prove nonresidency, so pruning conservatively leaves records resident.

Finite simulated-clock admission

The original SimClock advances once per admitted scheduler round, including a round that then observes all coroutines terminal. This is simulated model time. Worker waves do not create extra ticks and physical host time is not substituted. Both Rust backends preflight the tick successor and simulated Duration successor together. ClockExhaustion propagates as the source-bearing ProtocolMachineError::ClockExhausted before ingress, callbacks, progress, scheduler mutation or cooperative round-observation resets. Refusal changes neither original counter and does not acknowledge any pending operation.

Finite canonical effect-occurrence custody

Fresh canonical effect IDs require complete capacity admission before their host work or mutation. The finite counter never allocates its exhausted u64::MAX sentinel. The same selected threaded wave holds its summed bound before issuance. Actual producers retain deterministic allocation order. Already-issued original nominal Failure/Blocked observations use their original occurrence, while fresh issuance and positive publication retain their complete capacity obligations. Actual returned values and errors remain with the original continuation.

A topology poll and its dynamically returned batch have separate boundaries: admit the poll before calling it, then retain its actual outcome and admit the whole event batch before applying its first mutation. Timeout-witness capacity is independently required. Capacity refusal does not undo an already admitted round’s SimClock increment or scheduler selection. It is distinct from atomic clock-exhaustion admission above and supplies no asynchronous cleanup receipt. The prepared finite-representation deviation and executable acceptance scope are recorded in Rust-Lean parity.

The bounded authoritative nominal readiness path retains its actual typed result in the original coroutine continuation. Output/WAL gates and destination checks precede the canonical same-session witness consumer. Success publication and branch installation follow that consumer without a foreign callback or clock admission. Registry issuance and live original owner/generation/scope checks are mandatory even when register data has a readiness type. The Lean session model stores the corresponding semantic owner and issued/consumed evidence in its canonical session record. This is not a Rust host-resource proof.

Protocol-Machine Bytecode Instructions

This document defines the current protocol-machine instruction set in rust/machine/src/instr.rs.

Instruction Families

The protocol machine groups instructions by execution concern.

FamilyInstructions
CommunicationSend, Receive, Offer, Choose
Session lifecycleOpen, Close
Effect and guardInvoke, Acquire, Release
SpeculationFork, Join, Abort
Ownership and knowledgeTransfer, Tag, Check
ControlSet, Move, Jump, Spawn, Yield, Halt

Instruction Reference

All register operands are bounds-checked at runtime. Out-of-range reads or writes fail with Fault::OutOfRegisters.

Communication

InstructionFieldsRuntime effect
Sendchan, valEmits a send on the endpoint in chan. Payload routing is decided through the effect handler path.
Receivechan, dstReads one message from the partner edge and writes the payload to dst.
Offerchan, labelPerforms internal choice and emits the selected label.
Choosechan, tableReads a label and branches by jump table entry.

Session lifecycle

InstructionFieldsRuntime effect
Openroles, local_types, handlers, dstsAllocates a new session, initializes local type state per role, binds edge handlers, and writes endpoint handles to destination registers.
ClosesessionCloses the referenced session and emits close and epoch events.

Effect and guard

InstructionFieldsRuntime effect
InvokeactionExecutes an effect step through the bound handler for the session.
Acquirelayer, dstAttempts guard acquisition and writes evidence to dst when granted.
Releaselayer, evidenceReleases a guard layer using previously issued evidence.

Speculation

InstructionFieldsRuntime effect
ForkghostEnters speculation scope tied to a ghost session identifier.
JoinnoneCommits speculative state when reconciliation checks pass.
AbortnoneRestores scoped checkpoint state and exits speculation.

Ownership and knowledge

InstructionFieldsRuntime effect
Transferendpoint, target, bundleTransfers endpoint ownership and associated proof bundle to a target coroutine.
Tagfact, dstTags a local knowledge fact and returns the result in dst.
Checkknowledge, target, dstChecks a fact under the active flow policy and writes the check result to dst.

Control

InstructionFieldsRuntime effect
Setdst, valWrites an immediate value to a register.
Movedst, srcCopies a register value.
JumptargetPerforms an unconditional jump.
Spawntarget, argsSpawns a child coroutine with copied argument registers.
YieldnoneYields back to the scheduler.
HaltnoneTerminates coroutine execution.

Compilation From Local Types

rust/machine/src/compiler.rs exposes compile(local_type: &LocalTypeR) -> Vec<Instr>.

The compiler emits communication instructions, Invoke, and control-flow instructions. It does not emit session lifecycle, guard, speculation, or ownership opcodes.

LocalTypeR nodeEmission pattern
single-branch SendSend then Invoke then continuation
multi-branch Senddeterministic Offer on first branch then continuation
single-branch RecvReceive then Invoke then continuation
multi-branch RecvChoose with patched jump table then branch blocks
Murecords loop target then compiles body
VarJump to loop target if bound, otherwise Halt
EndHalt

The compiler is intentionally simple. Full ISA coverage is provided by direct bytecode construction and runtime loaders.

Session Lifecycle

This document defines session state and lifecycle behavior in rust/machine/src/session/mod.rs and rust/machine/src/engine/.

Session State Model

A session stores role membership, per-endpoint local types, directed buffers, edge handler bindings, trace data, lifecycle metadata, and host-runtime ownership state.

Field groupPurpose
sid, rolesSession identity and participant set
local_typesCurrent and original local type for each endpoint
buffersSigned directed edge buffers
edge_handlersPer-edge runtime handler binding
edge_traces, auth fieldsCoherence and authenticated trace material
status, epochLifecycle phase and close epoch counter
ownership statecurrent owner capability, transfer-in-progress state, issued readiness/cancellation/timeout witnesses, consumed witness ids, and terminal ownership reason

The protocol machine also tracks communication replay-consumption state at runtime scope (off, sequence, nullifier). This state is keyed by session-qualified edges and contributes to canonical replay artifacts.

Ownership fields are a runtime hardening contract rather than a theorem surface. They govern who may drive session-local host mutation and how ownership transfer is staged and audited.

OwnedSession uses the sealed owned::OwnedSessionMachine interface for both cooperative and threaded machines. Its public operations require an actual issued OwnedSession, rather than serializable capability fields. Both storage adapters borrow the original resident SessionState and execute the same ownership transitions. Threaded entry is exclusive with respect to machine execution. No worker wave remains active across that mutable entry. Host code does not receive a raw mutable threaded session store.

Owned host-frame ingress uses OwnedSession::inject_message_with_sequence. Both backends validate the live capability and full-session mutation scope in the same resident-session entry that signs and enqueues the frame. The supplied edge sequence is preserved. A stale, terminal, missing or fragment-scoped owner cannot enqueue through this boundary. Ownership rejections retain their typed source and the existing authority journal. Ownership and enqueue refusals remain distinct variants of OwnedSessionEnqueueError. Backpressure or drop results retain their actual EnqueueResult. This new API does not extend the existing exhaustive ProtocolMachineError enum. This entry does not advance endpoint local types, manufacture send-completion evidence, issue authority or acknowledge host-resource cleanup. It adds a host adapter for the existing signed-buffer operation, not a choreography instruction or a new Lean ownership theorem. The raw simulation injection API remains a separate testing surface.

Owned commit, rollback, owner-death and abandoned-transfer operations validate the calling handle’s live capability. Receipt operations also require the receipt’s session to match the calling handle, so possession of an unrelated session’s valid receipt cannot redirect an operation. Rollback removes only the matching staged claim and preserves its current owner. It does not undo a previously committed transfer or acknowledge application resource settlement.

Scope attenuation and transfer may preserve or reduce the actual owner’s scope. A fragment owner cannot recover session-wide authority or add other fragments. Session-local edge mutations reject an edge naming another session. Generations remain monotonic across release/reclaim, including restored ownership history. Generation, claim and witness counters fail before mutation when exhausted. Owner-death and abandoned-transfer paths allocate their required witness before changing terminal state. Exhaustion therefore leaves the original owner and staged transfer available for explicit recovery.

Authority records come from the canonical transition producer on both backends. The store-owned journal records actual cross-session emission order and stamps owned operations with the current machine tick. It retains genuine rejection records even when the attempted operation returns an error. Disposal order does not reorder that history. The threaded journal lock is acquired before mutation. Poisoning it cannot commit an ownership change without its required audit. Older cooperative snapshots with only per-session observations retain that representation without inventing historical emission times or stronger evidence. Targeted disposal retains those records as historical audit evidence, including across cooperative snapshot restoration. Repeated disposal does not duplicate them. Semantic objects, capability lifecycle exports and replay fragments include the retained records. They cannot reconstruct a live owner or resume a session.

Owned-loader ownership failures retain OwnershipError as the source of ProtocolMachineError::OwnershipContract, rather than formatting the cause into a string. Existing callers matching that variant must handle the typed payload.

Native worker-pool construction failures

Threaded construction preserves the original Rayon failure and its native I/O source in ProtocolMachineError::ThreadPoolBuild. Diagnostic text does not replace the original error chain. This host-construction error precedes admitted protocol sessions and changes no instruction, capability or protocol outcome.

A failed pool constructor can follow partial native worker startup. Rayon requests termination on failure but returns no worker-join acknowledgment. The production owned constructor therefore retains its original native handles alongside the actual Rayon cause. Only explicit NativeWorkerPoolBuildFailure::settle_original_workers issues the retained join receipt. Observing the constructor error or dropping a pool does not acknowledge settlement. Targeted session disposal leaves a live shared pool available and remains a separate acknowledgment.

The original direct-Rayon error fixtures establish their own host-owned joins. The canonical owned-constructor fixtures additionally exercise the production handle custodian. Neither fixture family proves signed protocol termination or settlement of unrelated application resources.

Session Status Values

SessionStatus includes Active, Draining, Closed, Cancelled, and Faulted.

Draining is currently a declared status only. The current SessionStore::close path sets Closed directly and clears buffers.

Required Targeted Disposal

ProtocolMachine::close_and_reap_session and ThreadedProtocolMachine::close_and_reap_session acknowledge removal of exactly one resident session and its runnable or blocked coroutine custody. A successful return leaves no target-session coroutine in live storage or scheduler queues. Session resources, pending handoffs, and communication-consumption state are retired, and outstanding target effects are invalidated. Unrelated sessions and the shared worker pool remain available.

The caller holds exclusive mutable machine ownership. Threaded rounds join every owned worker job before returning, so disposal acknowledges completed worker execution as well as removal of scheduling custody. This API neither interrupts an active handler from another thread nor creates an independent teardown worker.

Residency, stable coroutine indices, required locks, terminal count and epoch checks precede mutation. Failures return SessionDisposalError or ThreadedSessionLifecycleError, without a successful acknowledgment or partial target removal. Active closure requires a checked epoch increment. An exhausted active epoch fails closed. Disposal preserves the existing epoch and status of a naturally Closed, Cancelled or Faulted session. Repeated acknowledgment returns the original compact archived summary. Reaped coroutine identifiers remain stable for surviving sessions and are never interpreted as compacted array positions.

Compact summaries and historical observations follow the machine’s existing lifetime archival policy. They are diagnostic history, not resumable sessions or protocol authority. Target disposal does not clear shared guard resources or establish successful protocol completion, authenticated cancellation, or application-level terminal publication. Those obligations remain with the owner that drives the protocol before disposal.

Ownership Lifecycle

Session ownership is tracked separately from capability admission.

Ownership elementMeaning
current owner capabilitythe live owner label, generation, and authorized scope
ownership generationincrements on transfer or scope attenuation so stale handles fail closed
pending transfer receiptexplicit staged transfer that must commit or roll back
readiness witnesssingle-use proof that a protocol-critical check succeeded under one live owner generation
authority audit logdeterministic issuance, consumption, invalidation, rollback, rejection, and expiry history for first-class ownership/receipt/witness objects
terminal ownership reasonrecorded reason when owner death or transfer failure forces cancellation or fault

Default runtime rules:

  • at most one current owner exists for one active session ownership unit
  • transfer is explicit and uses a receipt
  • readiness witnesses are single-use and generation-bound
  • timeout activation stores and later expires the exact issued timeout witness rather than reconstructing timeout evidence from ambient clock state
  • rollback is claim-specific, so failing one staged transfer does not tear down unrelated ownership state
  • fragment-scoped ownership attenuates authority and does not imply full-session mutation rights

In the canonical capability taxonomy, this lifecycle section spans:

  • ownership capability state for the live owner
  • transition lifecycle state for transfer receipts and semantic handoff
  • evidence lifecycle state for readiness, cancellation, and timeout witnesses

Ownership Failure Mapping

Ownership failures map into session terminal behavior as follows.

Ownership failureRuntime behavior
owner deathFaulted { reason = "ownership owner ... died" }
abandoned transferCancelled
failed transfer commitFaulted { reason = "ownership transfer commit failed: ..." }
stale owner usetyped ownership error, fail-closed, no status change unless policy escalates
forged or reused readiness witnesstyped ownership error, fail-closed, audit rejection record

These mappings are implementation policy. They are not claims that the Lean theory proves host-runtime ownership outcomes directly.

Explicit Failure and Timeout Observability

Failure, timeout, and cancellation behavior is now explicit at the semantic-audit surface rather than inferred only from final status.

Observable eventMeaning
TimeoutIssueda timeout occurrence became active for one site and issued a timeout witness
CancellationRequesteda cancellation path was requested for one session with one cancellation witness
Cancelledthe explicit cancellation path completed for one session using the same witness
FailureBranchEntereda typed failure branch became visible before terminal fault handling
SessionTerminala session reached an explicit terminal state with a deterministic terminal reason

Deterministic ordering rules:

  • timeout activation is recorded as TimeoutIssued at the tick where the topology timeout becomes active
  • explicit cancellation emits CancellationRequested, then Cancelled, then SessionTerminal { Cancelled { ... } }
  • explicit abort emits Aborted, then SessionTerminal { Aborted { ... } }
  • explicit close emits Closed, then SessionTerminal { Closed { ... } }, then EpochAdvanced
  • coroutine fault handling emits FailureBranchEntered before Faulted

These events are part of replay-visible observability. Host integrations should not reconstruct this ordering indirectly from final statuses.

Canonical replay artifacts retain these lifecycle-visible events through semantic_audit_log. That surface also includes authority witness issuance/consumption and delegation completion records. The stricter lifecycle export for protocol-critical ownership, receipt, and witness objects is capability_lifecycle_audit_log(). Embedders and simulator harnesses should prefer these canonical audit surfaces over custom post-hoc reconstruction from raw logs. Choreography-level timeout ... on timeout ... on cancel ... lowering maps its protocol-visible timeout and cancellation outcomes to this same explicit lifecycle event family.

Semantic Handoff and Transformation Obligations

Committed delegation is now exported as a first-class semantic handoff surface, not only as a low-level transfer receipt.

Semantic objectMeaning
SemanticHandoffreplay-visible owner transition with explicit revoked and activated owners
TransformationObligationbundle of fragment, operation, effect, witness, and publication obligations induced by the handoff

Runtime rules for committed handoff:

  • old-owner revocation and new-owner activation are explicit semantic events
  • publication authority transfers from revoked_owner_id to activated_owner_id
  • pending outstanding effects in the affected session are rebound to the new owner
  • blocked outstanding effects are invalidated and become late-result-rejecting semantic artifacts
  • affected OperationInstance values gain an explicit dependency edge on the handoff receipt and are rebound or failed closed as required by status

TransformationObligation is the canonical export for the resulting transformation-local obligations. Each obligation records:

  • transformed fragments
  • affected operation ids
  • affected effect ids
  • transported effect ids
  • invalidated effect ids
  • witness transport or invalidation policy
  • publication revocation and activation owners
  • scope, tick, and status

Late results arriving after invalidation or post-handoff revocation must fail closed. Embedders and simulator harnesses should treat these invalidation artifacts as canonical semantic audit data rather than reconstructing them from raw effect trace order.

Open Path

Open is executed by protocol machine::step_open. The instruction carries roles, local_types, handlers, and dsts.

Open admission checks enforce role uniqueness and full handler coverage across the opened role set. Arity must match between local_types and dsts.

On success the protocol machine allocates a fresh session, initializes buffers and local type entries, stores edge handlers, writes endpoint values to destination registers, and emits an Opened event.

Preferred host integration path:

  • ownership-bearing open: load_choreography_owned(...)

The open path immediately claims session ownership. It is the public integration route for hosts that will mutate session-local runtime metadata. Hosts that need to materialize protocol-critical checks should issue explicit readiness witnesses through that ownership-bearing path. Language-level check Effect.op(...) forms are expected to lower into this same readiness/evidence machinery rather than inventing a separate host-visible authority cache.

Type Advancement

The session store is the source of truth for endpoint type state. Runtime step logic calls lookup_type, update_type, update_original, and remove_type.

Recursive progression uses unfold_mu, unfold_if_var, and unfold_if_var_with_scope. This keeps Var continuations aligned with the active recursive body.

Buffers and Backpressure

Each directed edge has a BoundedBuffer configured by BufferConfig.

Config axisValues
ModeFifo, LatestValue
Backpressure policyBlock, Drop, Error, Resize { max_capacity }

Signed send and receive paths use endpoint-specific verification keys. Auth tree fields track per-edge authenticated history.

Communication Replay-Consumption Lifecycle

Communication replay-consumption is separate from resource nullifiers and is enforced at communication boundaries.

PhaseRuntime locationBehavior
Create sequence identitysend path (step_send)Allocates sequence_no per session-qualified edge and writes it into signed transport payloads
Verify and consumereceive path (step_recv)Verifies signature first, then applies replay policy (off, sequence, nullifier) on canonical identity
Record proof artifactreceive commit pathAppends pre-root/post-root artifact entries for recursive proof composition
Finalize on closesession close pathPrunes per-session replay counters from in-memory tracker state

Policy semantics:

  • off: replay-consumption checks disabled.
  • sequence: requires exact next sequence number per edge.
  • nullifier: rejects duplicate canonical identities via consumed nullifier set.

Nullifier identity (ProtocolMachineConfig.communication_nullifier_identity):

  • sequence_bound (default): the nullifier covers the full canonical identity, including sequence_no. A resent message with a fresh sequence number derives a fresh nullifier and is accepted.
  • content_only: the nullifier covers the domain tag, sid, sender, receiver, step kind, label, and payload digest. sequence_no is excluded, so a resend of identical content is rejected with DuplicateIdentity. Protocols that legitimately repeat identical content on one edge and label must distinguish those payloads.

Hash model (ProtocolMachineConfig.communication_hash_model):

  • HashModel::DEFAULT uses the built-in portable pseudo-hash, which is deterministic but not collision resistant.
  • Security-sensitive embedders supply a cryptographic hash with HashModel::new(id, hash_fn) or HashModel::from_verification_model::<V>(id). The model computes payload digests, receive nullifiers, and the replay root.
  • Serialized configs and machines record only the model id. Deserialization rejects any id other than the default, so a custom model is never silently replaced by the pseudo-hash. Re-supply custom models programmatically after restore.

Consumed nullifiers are never pruned. Session close drops only per-edge sequence counters, so closing a session cannot re-admit a consumed identity.

Close Path

Close is executed by protocol machine::step_close and then SessionStore::close.

The protocol machine first checks endpoint ownership for the closing coroutine. If ownership is valid, the store sets status = Closed, clears buffers and edge traces, and increments epoch.

Close emits Closed, SessionTerminal { Closed { ... } }, and EpochAdvanced observable events. There is no automatic draining loop in the current close implementation.

The close path is distinct from host-runtime ownership transfer. Endpoint/coroutine ownership for bytecode execution remains part of normal protocol-machine execution semantics. Host-runtime ownership governs who may mutate session-local host state at the embedding boundary.

Migration and Operations

Default behavior:

  • ProtocolMachineConfig.communication_replay_mode defaults to off, preserving prior runtime behavior.
  • Existing workloads continue to run without replay-consumption enforcement until mode is changed.

Opt-in guidance:

  • Local development: set communication_replay_mode = sequence to catch reorder/duplicate transport issues early.
  • Strict zk-oriented traces: set communication_replay_mode = nullifier to force one-time identity consumption checks.
  • Content replay protection: combine nullifier mode with communication_nullifier_identity = content_only and a cryptographic communication_hash_model.

Why this matters for consensus protocols:

  • Protocol-level BFT logic can tolerate many faults. However, zk proofs of full executions require transport-level one-time consumption to prevent replay-equivalent transcript ambiguity.
  • Enabling replay-consumption closes this gap between protocol safety and proof transcript soundness.

Configuration examples:

  • Local test config: ProtocolMachineConfig { communication_replay_mode: Sequence, ..ProtocolMachineConfig::default() }
  • CI parity fixture config: set communication_replay_mode in both cooperative and threaded runners and compare canonical replay fragments.

Risk notes:

  • Performance overhead: replay checks add per-receive hashing and state updates.
  • State growth: sequence maps and nullifier sets grow with message volume, session close prunes per-session sequence counters.
  • Rollback/restart behavior: replay roots and consumption artifacts are included in canonical replay fragments and should be persisted with other replay evidence.

Required host integration coverage is part of just check-session-disposal: the Rust-native inventory requires all 35 nonignored declarations, actual harness discovery and successful execution across the two binary targets. The ownership aggregate reuses that gate. Opaque-handle and metadata-substitution compile-fail doctests additionally protect the public owned API. CI acceptance runs with CARGO_INCREMENTAL=0. This does not change the host’s independent cleanup duties.

Explicit private worker-pool settlement

ThreadedProtocolMachine::shutdown_worker_pool consumes the original machine, requests termination of its private Rayon pool and joins every original native worker handle retained by the production spawn handler. The returned opaque NativeWorkerPoolDisposal acknowledges those joins, including workers that terminated with a retained original panic payload. It does not certify protocol success, signed-terminal publication, or host resource settlement outside that pool. Callers settle their session obligations before consuming the machine.

This host operation blocks until the actual workers exit. It cannot interrupt a handler. Ordinary pool Drop remains an unacknowledged termination request. Construction failures retain the actual Rayon error and partial native worker handles in NativeWorkerPoolBuildFailure. Explicit settle_original_workers joins those original handles and retains the first actual disposal receipt. Repeated receipt observation neither restarts workers nor consumes panic payloads.

Both normal shutdown and failed construction use the same resident native-worker settlement custodian. It removes only the next original handle at the consuming join boundary, without holding its state mutex across the join. Actual worker panic results acknowledge their completed joins and retain original payloads. A panic of the join operation itself instead returns retained custody and the exact panic payload. Its consumed handle remains permanently unproven. Untouched original handles, completed-join counts and chronological independent causes survive for same-original retry. Settling those remaining workers cannot erase the earlier uncertainty or issue a whole-pool acknowledgment.

NativeWorkerPoolShutdownFailure::OriginalWorker retains the untouched machine. NativeWorkerPoolShutdownFailure::Unproven retains the native settlement owner. Failed-constructor settlement returns that same resident owner by reference on refusal. Every settlement entry rejects an actual original-worker caller, including a still-live worker whose consumed handle has\nunproven settlement, or a concurrent settler before consuming another handle. Progress snapshots expose observations and retained causes, never reconstructed authority or cleanup ACK. No external callback runs under the resident state mutex. The host operation is still blocking and cannot interrupt a stuck worker or provide async Drop cleanup.

Eleven native component fixtures are required. Five additions exercise injected unwind at the canonical consuming join boundary, preservation of earlier and later worker panic causes with real pending workers, same-original remaining joins, actual-worker refusal, retained consumed-worker identity across unwind, and\nobservation during a pending join. Injection does not establish an actual operating-system deadlock refusal or settlement of the handle whose join unwound. Actual compilation and execution remain required.

Shutdown checks the actual original worker handles before requesting termination. A caller executing on one of those workers receives the unchanged original machine in the refusal result and must transfer it to an external owning context for same-original retry. This prevents self-join without discarding custody.

CARGO_INCREMENTAL=0. This does not change the host’s independent cleanup duties.

Typed Host Outcome State Commit

InvokeStep and Receive results update coroutine registers only after successful status and exact response-kind acceptance. The failed instruction does not advance its continuation, and an attached payload on a failure or blocked result does not replace the original register file. A raw successful exchange with the wrong response kind remains observable but is a Failed lifecycle result and cannot publish effect.succeeded.

This state-commit rule is not a receive-buffer rollback guarantee. It does not undo a dequeued message, validate a domain label, acknowledge a host future, or implement a generation-bound asynchronous completion. Those are separate protocol and host-resource obligations.

VM Invoke and Receive requests retain their actual configured register width as optional instruction context. All VM state-replacing responses, including branch Choose/Receive, must preserve that width before register replacement or effect- success publication. This enforces the canonical Rust wf_coroutine_state register-width invariant, not an inferred equal-length convention. Receive also retains its actual destination and requires that register to exist. Branch receive omits the destination because it performs no destination write, but retains width. After existing signature, replay and payload validation, both VM receive backends reject an out-of-range instruction destination with OutOfRegisters before invoking the host receive handler. This preserves the VM fault taxonomy and prevents host receive mutation for that invalid destination. It does not roll back earlier dequeue or replay work. Handler-only wrappers omit both instruction contexts. Missing fields on older serialized requests decode as absent and do not invent historical VM context. The original raw outcome remains observational evidence. This check is not rollback of earlier buffer dequeue, replay consumption, clock or type work. Optional observability retention cannot replace an original host failure: the canonical recorded exchange supplies lifecycle status without a second synthetic issue/complete cycle. Blocked remains a Blocked observation while the synchronous instruction reports its existing blocked-contract fault.

Host-response acceptance is distinct from whole-instruction commit. In particular, Invoke may apply its accepted register state and host-success observation before persistence-delta application faults. No rollback of that state, emitted events, or earlier communication work is implied by the response-kind/destination guard. The prior cooperative owner could evict live enforcement inputs under diagnostic retention, while the prior threaded owner used untrimmed vectors. The canonical live/resident retention rule below repairs that storage boundary in both existing owners. Response/error preservation alone was insufficient. Neither correction claims nominal authority, a disposal acknowledgment or a Lean correspondence proof.

Serialized request context is an observation used to validate response shape, not proof of original request ownership or nominal authority. The replay handler selects by expected entry kind. It does not establish full incoming request identity. Actual VM producers attach current configured width before execution, including when replay supplies the response. Lean’s abstract effect-context step and spawn register copying are not a proof of this concrete Rust register ABI.

Canonical host exchanges also supply Acquire/Release/SendDecision outcome observations directly. Duplicate synthetic issue/complete cycles must not replace the original typed failure under optional diagnostic retention. Existing policy is preserved: cooperative Acquire Blocked waits on AcquireDenied, threaded Acquire Blocked reports its synchronous blocked-contract fault, and Release/SendDecision Blocked remain synchronous faults. The separate Send buffer WouldBlock producer retains its actual live capacity obligation: successful host decision is not queue admission, so that issue/complete pair is not a duplicate host outcome.

Live outstanding effects and their operation/progress records survive Disabled and Capped observability settings in the same canonical owner fields. Targeted session invalidation updates those originals before terminal-history pruning. Other live sessions remain untouched. Retention pruning is not a completion, cleanup acknowledgment, owner transfer or proof that a host future stopped.

Resident-session terminal records remain canonical inputs to agreement/WAL gates and cannot be discarded as history. Successful targeted reap or actual closed session reap establishes their nonresidency before terminal-history pruning. The next real WalSync gate must see the same original operation and evidence under Full, Capped1 and Disabled retention. Loss must not silently select the synthetic fallback operation or its default agreement level.

The composed required native ownership/disposal inventory contains 59 unique fixtures: the original 35 plus 17 strict-outcome/error, six live/resident retention and one cooperative full-queue/Disabled regression. The existing canonical checker requires their nonignored declarations, actual harness discovery and successful execution. Declarations alone are not passing evidence. Module filters can also execute required fixtures appearing in a narrower sub-suite, without changing the unique required count. Authority guard DSL/compiler/bridge fixtures and formal examples remain separate targets and obligations.

Authoritative-read reference identity

Readiness and cancellation witness numbers are local to a session. New semantic exports use opaque content references covering every exported AuthoritativeRead field except read_id itself: session, owner, kind, lifecycle, predicate, witness number, generation and reason. Fields have explicit None/Some discriminants and UTF-8 byte-length framing. Equal complete content may alias. Distinct complete content does not, including distinct rejected submissions for one witness. Operation and region read references use those same canonical producer IDs. These observations do not replace the actual issued witness or its consumption checks and confer no additional runtime authority.

The v1 semantic-object field schema remains readable: existing unambiguous serialized references keep their original bytes and can still be looked up. Lookup accepts repeated identical read records, but rejects different records under one reference rather than choosing the first. This includes ambiguous historical cross-session references. No migration reconstructs witness authority from a reference. Consumers must not parse read-reference strings as a stable wire grammar. Newly generated references are deliberately session-qualified. Output-condition references remain content-addressed by predicate and output digest: identical reobservations are allowed. Contradictory records refuse lookup. This does not assign an occurrence identity or claim that every check has a distinct audit event.

Timeout-witness identifier exhaustion

The cooperative topology timeout producer allocates witness numbers within its original machine, separately from session-local readiness/cancellation numbers. It refuses an exhausted successor before replacing an existing timeout, changing eligibility, or publishing timeout evidence. A returned topology batch is preflighted for all required witness numbers before any event in that batch is applied. The host callback’s already-observed outcome remains retained. Refusal does not undo that observation, renew a window or acknowledge pending work. The existing contract-violation error reports exhaustion. The threaded topology path currently records timeout horizons without issuing these TimeoutWitness objects. This correction does not invent threaded witness parity or new physical-time behavior. SimClock admission and original timeout horizons remain unchanged.

Finite session-identifier admission

Rust session identifiers are finite usize values. Fresh open reserves the maximum value as its exhausted high-water mark, and rejects that boundary before inserting a session or replacing an existing resident. Explicit-id open is fresh allocation, not restoration: it rejects an id below the original high-water mark or already in the resident map. Restore/resume retains the original complete store and counter. It must not replay fresh open with a previous session id.

Both stores use the same checked admission rule. The cooperative exclusive borrow and threaded existing map write lock cover counter admission plus insertion. Failure preserves the original counter, resident session, capability and original outcomes. Host loading returns the typed SessionAllocationError. Source Open retains it in Fault::SessionAllocation, classified under the existing close fault category and propagated through the canonical fault/trace path. No external handler is called by this allocation boundary. Existing arity, role and handler checks precede allocation as before. A public round can already have advanced its selected clock and run topology ingress before dispatch. This refusal does not rewind those actual observations. No Opened occurrence is produced for a refused allocation.

OwnedSession private construction prevents callers from fabricating a handle, but its resident capability check is not an independent per-machine nonce. Fresh session ids must therefore remain fresh. Resetting a session by overwriting its id could reset its resident ownership generation, and must not be treated as a safe way to retire or restore original custody. This finite-admission preparation is not a proof of arbitrary serialized snapshot provenance or cross-machine owner scope.

Simulation Overview

This page is the top-level guide for telltale-simulator. It describes the supported simulator surface at a high level. Detailed behavior lives in the focused pages linked below.

Scope

The simulator runs projected local types on telltale-machine. It adds deterministic middleware for budgeted adversaries, network behavior, property monitoring, checkpointing, and replay artifacts. It also provides a harness API for external integration testing. The authoritative replay lane is explicit: fresh exact runs use run_with_scenario(...), canonical exact reproduction uses run_canonical_replay(...), exact non-durable checkpoint resume uses resume_with_checkpoint_artifact(...), and durable scenarios resume through resume_with_durable_checkpoint_artifact(...) with typed WAL and evidence artifacts. The simulator also exposes a dedicated durability assurance surface for fault-injecting WAL backends, deterministic crash/recovery comparisons, and durable property monitoring. On-disk checkpoint and replay bundles now use the typed PersistedReplayArtifact contract rather than ad hoc raw CBOR machine dumps.

Key Concepts

The simulator wraps the protocol machine defined in telltale-machine. The protocol machine owns scheduling and session-type enforcement. The simulator adds middleware layers (adversaries, network, properties, checkpoints) and environment dynamics (fields) around that core. See Protocol Machine Architecture for the underlying execution model. Durable agreement WALs and evidence-scoped recovery metadata remain authoritative machine/runtime artifacts. Simulator reports and viewer projections should consume typed durable artifacts rather than minting a parallel simulator-local durability state model.

ObsEvent is the protocol machine’s trace of communication actions such as sends, receives, choices, and offers. Scenario execution order, property monitoring, and replay artifacts all operate over this event stream.

FixedQ32 is the fixed-point numeric type (Q32.32) used for all simulator state values including field state, network parameters, and property thresholds. Quoted decimal strings like "1.5" are the safest TOML representation.

ProtocolMachineSemanticObjects is the typed introspection snapshot the protocol machine exports after execution. It contains operation instances, outstanding effects, semantic handoffs, authoritative reads, materialization proofs, canonical handles, publication events, and progress contracts. Replay artifacts and post-run analysis consume this bundle directly.

Field and Environment Models

Fields are the simulator’s abstraction for deterministic environment-dynamics evolution. A field model defines how role-local numeric state changes when the protocol machine invokes EffectHandler::step, how an effect handler is constructed, and how default per-role initial states are derived. See Simulation Fields for the FieldModel trait, built-in field families, and environment extension hooks.

Topology, medium behavior, mobility, capability limits, and link admission live beside the field layer as separate environment hooks. The shared execution core consumes EnvironmentSnapshot and emits EnvironmentTrace without baking domain-specific naming into core Scenario.

Execution and Theorem Profiles

Execution policy is explicit through Scenario.execution, which separates backend choice from scheduler policy, scheduler concurrency, and worker-thread count. The simulator also exposes a separate theorem/profile layer through Scenario.theorem. That separation lets one execution be interpreted under different theorem-side contracts without changing the runtime behavior itself. See Simulation Scenarios for the full schema and backend resolution rules.

Reporting and Analysis

ScenarioStats includes theorem-native progress, reconfiguration accounting, and adversary budget summaries as separate fields. Replay artifacts retain the resolved adversary program and budget-consumption history so theorem-side assumption failures are inspectable after the fact. ScenarioResult.analysis.normalized_observability provides the companion envelope-aware analysis view for order-insensitive and footprint-aware comparison. Batch, sweep, and approximation manifests now also record execution-regime classification instead of requiring downstream tools to infer it indirectly.

The shared viewer stack sits directly on top of those artifacts. telltale-viewer owns the pure model/query/command layer, telltale-ui owns the portable Dioxus shell and reusable components, and telltale-web owns the browser packaging. The preferred human-facing inspection path is now the shared viewer rather than ad hoc textual replay output. That viewer now exposes:

  • a graph workspace for choreography, instantiated protocol, execution timeline, and branch-lineage projections
  • deterministic step-forward, step-backward, and jump-to-step time travel
  • typed branch create/update/delete flows
  • an insight workspace for regime display, watch expressions, run diff, causality, provenance, bookmarks, and archive reload
  • semantic comparison and first-divergence analysis over generic simulator artifacts
  • theorem-aware counterexamples for comparison and theorem-eligibility failures
  • deterministic sweep and suite exploration with drill-down into runs and branches
  • typed effect inspection and mocked rerun requests
  • deterministic minimization summaries and downstream extension slots

Decision and Approximation Modules

The decision module provides offline theorem-facing checks that return structured certificates and counterexamples for coherence, subtyping, capacity predicates, and theorem-profile eligibility. The approximation module provides non-authoritative analysis runs for batched_stochastic, mean_field, and continuum_field families. Approximation artifacts declare an approximation family, theorem-side scope, and explicit non-goals. Nested distributed simulation now publishes its own explicit observed-only manifest classification rather than remaining unclassified. That observed-only classification now travels through DistributedRunResult, not a manifest-only side accessor.

Quick Start

Use SimulationHarness with a HostAdapter implementation and a HarnessSpec.

#![allow(unused)]
fn main() {
let adapter = FieldAdapter::from_scenario(&spec.scenario)?;
let harness = SimulationHarness::new(&adapter);
let result = harness.run(&spec)?;
assert_contracts(&result, &ContractCheckConfig::default())?;
}

This path runs protocol-machine execution, scenario middleware, replay capture, and post-run contract checks. It is the recommended integration lane for external projects.

DirectAdapter is for hosts that already own the EffectHandler. FieldAdapter::from_scenario(...) constructs the handler and initial states from built-in scenario field parameters. See Simulation Fields for custom FieldModel integration via FieldAdapter::new(...) and FieldAdapter::from_boxed_model(...).

Generated Effect Helpers

The simulator exposes generated effect-family helper types under telltale_simulator::generated, such as GeneratedEffectScenario. Callers obtain a builder via GeneratedEffectScenario::builder() and chain outcome declarations before running. These helper APIs sit beside the harness API rather than inside it, and their helper reports intentionally do not expose theorem profiles, checkpoints, normalized observability, or other authoritative simulator-only fields.

Document Map

  • Simulation Runner: execution mechanics, stats, harness API, batch/sweep, distributed simulation
  • Simulation Scenarios: TOML schema, adversaries, reconfiguration, network, properties, checkpointing
  • Simulation Fields: field model trait, built-in families, environment extension hooks
  • Simulation Viewer: pure artifact envelopes, query/command boundary, branch patch model
  • Simulation Viewer Webapp: Dioxus shell, graph/time-travel workspace, run-insight workspace, ownership markers, testing split, local development

CLI

Use the simulator runner binary through just for CI-friendly JSON output.

just sim-run artifacts/sim_integration.toml

This command runs one scenario through the simulator entrypoint and emits the same authoritative artifacts the harness APIs produce.

Simulation Runner

This page documents runner behavior in telltale-simulator. It covers traces, runner entry points, stats, harness API, and scenario round order.

Core Data Types

Trace is the sampled role-state output container. Each StepRecord stores one role snapshot at one sampled step. ScenarioResult adds property violations, replay artifacts, and run statistics.

#![allow(unused)]
fn main() {
pub struct StepRecord {
    pub step: u64,
    pub role: String,
    pub state: Vec<FixedQ32>,
}

pub struct Trace {
    pub records: Vec<StepRecord>,
}

pub struct ScenarioResult {
    pub trace: Trace,
    pub violations: Vec<PropertyViolation>,
    pub replay: ScenarioReplayArtifact,
    pub stats: ScenarioStats,
}
}

step is a simulator sampling index. state contains the numeric portion of the coroutine register file. The runner skips the first two reserved registers and samples field-backed state starting at register 2.

ScenarioReplayArtifact contains the resolved adversary schedule, observable events, effect traces, semantic audit records, ProtocolMachineSemanticObjects, the canonical reconfiguration trace, and the canonical EnvironmentTrace. These artifacts support deterministic replay and post-run validation. PersistedReplayArtifact is the typed on-disk wrapper for replayable run data and exact checkpoint artifacts.

Runner Entry Points

The runner exposes four main entry points.

  • run(...) executes one choreography and returns one sampled trace.
  • run_multi_session_canonical(...) executes multiple choreographies on one canonical protocol machine and returns one trace per input choreography.
  • run_with_scenario(...) executes one choreography with scenario middleware and returns ScenarioResult.
  • run_canonical_replay(...) re-executes one scenario through the authoritative canonical replay lane even when the original scenario was run under another exact backend.

Use run_with_scenario(...) when adversaries, network behavior, properties, checkpoints, or replay artifacts are required. Use run_canonical_replay(...) when exact artifact reproduction through the canonical lane matters. Use the smaller entry points when only sampled state traces are needed.

ScenarioStats.execution_regime records the proof-side concurrency class for the resolved run. canonical_exact means single-step cooperative execution. threaded_exact means threaded execution at concurrency 1, which is theorem-equal to canonical. threaded_envelope_bounded means threaded execution at concurrency greater than 1, which is only authoritative modulo the declared envelope.

Stats and Summaries

Theorem Progress

ScenarioStats.theorem_progress reports theorem-native quantities that summarize the run in terms of the weighted-measure and scheduling-bound proofs. Depth is the sum of remaining session-type steps to end across all active sessions. Buffer is the total pending message count across all session buffers. The weighted measure is W = 2 * depth + buffer, a composite termination metric that strictly decreases on productive steps.

The reported fields are initial_weighted_measure, initial_depth_budget, productive_step_count, remaining_weighted_measure, weighted_measure_consumed, and critical_capacity. Critical-capacity phase classifies whether productive steps stayed below, at, or above the initial depth budget. This classification is unsupported for recursive protocols.

Other Summaries

ScenarioStats.scheduler_profile records the scheduler-facing report including implementation policy, productive exactness, total-step mode, and envelope status. ScenarioStats.reconfiguration_summary reports applied, pure, and transition operations with transition budget consumed. ScenarioStats.adversary_summary and ScenarioStats.assumption_diagnostics report adversary activation, budget consumption, and theorem-side assumption failures.

Observability Comparison

compare_observability(...) reports one of three relations: exact_raw_match, equivalent_under_normalization, or safety_visible_divergence. Normalization is order-insensitive over session-normalized observable events and canonical reconfiguration footprints.

Harness API

SimulationHarness is the stable integration path for external projects. It runs HarnessSpec or HarnessConfig through a HostAdapter. HostAdapter provides an EffectHandler, optional initial states, optional environment models, and a result validation callback.

DirectAdapter wraps an existing EffectHandler. FieldAdapter derives initial states and constructs the handler from scenario field parameters. See Simulation Fields for field adapter variants and custom FieldModel integration.

Batch and Sweep

run_batch(...) and run_batch_with(...) run many HarnessSpec values concurrently while preserving result order. BatchRunResult.manifest records one resolved execution-regime plus theorem-profile entry per input spec.

run_sweep(...) extends batch execution into deterministic parameter sweeps over seed, capacity_budget, scheduler_profile, reconfiguration_program, and adversary_budget. SweepRunResult.manifest records parameter bindings, execution-regime classification, theorem profiles, eligibility witnesses, and capacity-predicate reports per expanded run. Use compare_sweep_results(...) to diff experiment families by theorem eligibility and productive-step deltas.

Distributed Simulation

DistributedSimBuilder::execution_contract(...) accepts a NestedExecutionContract for outer scheduler concurrency plus inner rounds-per-step. That outer/inner VM contract is part of simulation semantics, not a worker-pool tuning knob. DistributedSimulation::run(...) now returns DistributedRunResult, which carries the observed-only classification manifest plus outer-VM trace data and per-site nested results in one aligned report surface.

Sampling and Step Mapping

The simulator uses explicit round-based sampling. If steps > 0, the runner records an initial sample at step 0 before the first protocol-machine round. Each subsequent completed round records one additional sample. If no samples were produced during execution, the runner emits one fallback sample at the last requested step index.

Scenario Execution Order

Scenario runs and replay share the same execution core and use a fixed per-round order for determinism.

  1. Compute next_tick from the protocol-machine clock.
  2. Activate due simulator reconfiguration operations from newly visible observable events.
  3. Advance the adversary program from newly visible observable events.
  4. Deliver due delayed adversary messages.
  5. When network middleware is active, route adversary-delayed messages through the network policy stage before they enter protocol-machine buffers.
  6. Deliver due network middleware queues.
  7. Update paused roles from active crash adversaries.
  8. Execute one protocol-machine round with the selected handler domain.
  9. Record one round-based trace sample when sampling is enabled.
  10. Run online property checks.
  11. Attempt checkpoint persistence when the interval policy triggers.

Checkpoint persistence is best-effort. Serialization and file-write failures do not fail the run. Persisted checkpoint files are typed PersistedReplayArtifact CBOR payloads. Checkpoint resume through resume_with_checkpoint_artifact(...) restores middleware state, so adversary, network, and reconfiguration behavior continue exactly from the captured tick.

Helper Boundary

Generated effect-family helpers remain non-authoritative support APIs. GeneratedEffectSimulationReport is intentionally narrow: it exposes the scripted helper scenario, semantic objects, and semantic audit log through helper accessors, but not theorem profiles, replay contracts, normalized observability, or checkpoint data.

Local Workflow

Repository-wide CI remains just ci-dry-run. For staged diffs that are restricted to the simulator subsystem, the narrower local gate is now just check-simulator-subsystem-staged. That path still enforces Rust formatting, simulator compile/test coverage, and simulator-doc link checks without being blocked by unrelated pre-existing breakage elsewhere in the workspace.

Determinism and Reproducibility

Simulator randomness is scoped to SimRng, seeded from scenario.seed and currently backed by ChaCha8. The protocol machine remains deterministic given the same handler outcomes and scheduler inputs. The canonical backend remains the authoritative replay and debugging lane.

Record ordering is stable within each sampling pass. Replay artifacts preserve the observable, semantic, and reconfiguration data needed for deterministic post-run inspection.

Simulation Scenarios

This page documents scenario configuration and middleware behavior. It covers the TOML schema, budgeted adversary declarations, first-class reconfiguration, network modeling, properties, checkpointing, and current limits.

Scenario Schema

Scenario is parsed from TOML and drives run_with_scenario(...). seed defaults to 0. field is optional and is only required by built-in field-driven surfaces such as FieldAdapter::from_scenario(...), derive_initial_states(&Scenario), and the simulator CLI binaries. Execution defaults are resolved through Scenario.execution. backend = "auto" resolves to the authoritative canonical lane with scheduler_concurrency = 1 and worker_threads = 1. Theorem-facing interpretation is configured separately through Scenario.theorem.

#![allow(unused)]
fn main() {
pub struct Scenario {
    pub name: String,
    pub roles: Vec<String>,
    pub steps: u64,
    pub execution: ExecutionSpec,
    pub seed: u64,
    pub network: Option<NetworkSpec>,
    pub field: Option<FieldSpec>,
    pub reconfigurations: Vec<ReconfigurationSpec>,
    pub adversaries: Vec<AdversarySpec>,
    pub properties: Option<PropertiesSpec>,
    pub checkpoint_interval: Option<u64>,
    pub theorem: TheoremProfileSpec,
    pub durability: DurabilitySpec,
    pub extensions: BTreeMap<String, toml::Value>,
}
}

network, field, reconfigurations, adversaries, properties, checkpoint_interval, durability, and extensions are optional.

#![allow(unused)]
fn main() {
pub struct ExecutionSpec {
    pub backend: ExecutionBackend,
    pub scheduler_policy: SchedulerPolicySpec,
    pub scheduler_concurrency: Option<u64>,
    pub worker_threads: Option<u64>,
}
}

scheduler_policy selects the underlying protocol-machine scheduling discipline. scheduler_concurrency controls how much scheduler work one simulator round may admit. worker_threads controls physical parallelism for the threaded backend only. Canonical execution requires both values to resolve to 1. Threaded execution with scheduler_concurrency = 1 remains exact with respect to the canonical lane, while scheduler_concurrency > 1 is only authoritative modulo the declared concurrency envelope.

#![allow(unused)]
fn main() {
pub struct TheoremProfileSpec {
    pub scheduler_profile: TheoremSchedulerProfile,
    pub envelope_profile: TheoremEnvelopeProfile,
    pub assumption_bundle: TheoremAssumptionBundle,
}
}

The theorem block does not change runtime execution. It declares which theorem-side contract the caller wants the run to be interpreted under. The simulator resolves that declaration against the actual execution regime and reports eligibility in ScenarioStats.theorem_profile and ScenarioReplayArtifact.theorem_profile.

#![allow(unused)]
fn main() {
pub struct DurabilitySpec {
    pub mode: DurabilityMode,
}
}

durability.mode = "wal" declares that checkpoint resume is only valid when the caller also supplies typed durable WAL and evidence-cache artifacts through resume_with_durable_checkpoint_artifact(...). Generic checkpoint resume fails closed for those scenarios so the simulator cannot silently ignore the durable contract.

For recursion-free scenarios, the simulator also derives theorem-native progress quantities in ScenarioStats.theorem_progress. See Simulation Runner for definitions of the weighted measure, depth, buffer, and critical-capacity phase.

Scenario Example

name = "mean_field_adversary_window"
roles = ["A", "B"]
steps = 200
seed = 42
checkpoint_interval = 25

[durability]
mode = "wal"

[execution]
backend = "threaded"
scheduler_policy = "round_robin"
scheduler_concurrency = 2
worker_threads = 4

[field]
layer = "mean_field"

[field.params]
beta = "1.5"
species = ["up", "down"]
initial_state = ["0.6", "0.4"]
step_size = "0.01"

[network]
base_latency_ms = 20
latency_variance = "0.10"
loss_probability = "0.02"

[[reconfigurations]]
trigger = { at_tick = 50 }
action = { type = "link", from = "A", to = "B", enabled = false }

[[adversaries]]
id = "loss_window"
trigger = { at_tick = 75 }
action = { type = "withholding" }
budget = { total = 16, mode = "windowed", window_ticks = 8, max_per_window = 2 }

[properties]
invariants = ["no_faults", "simplex", "buffer_bound(0,16)"]

Quoted decimal strings are the safest TOML form for FixedQ32 values. The parser also accepts compatible numeric representations.

Adversary Middleware

AdversaryInjector wraps the inner handler. It manages trigger activation, budget accounting, delayed delivery, corruption, withholding, correlated bursts, and crash state.

Supported actions are withholding, timing_disturbance, corruption, crash, and byzantine_interference. Supported triggers are Immediate, AtTick, AfterStep, Random, and OnEvent. If a trigger declaration sets no trigger field, it defaults to Immediate.

An adversary must not set more than one trigger field at once. The parser rejects multi-trigger declarations. Trigger::AfterStep is evaluated against logical round count rather than raw tick count.

Adversary Budgets

Every adversary must declare a budget.

#![allow(unused)]
fn main() {
pub struct AdversarySpec {
    pub id: Option<String>,
    pub trigger: TriggerSpec,
    pub action: AdversaryActionSpec,
    pub budget: AdversaryBudgetSpec,
}
}

budget.total is the total disturbance or activation budget. budget.mode can be activation (one-shot), independent (per-message Bernoulli), windowed (per-window correlated quotas), or correlated (burst-style windows). budget.assumption_failure optionally states which theorem-side clause should fail if the budget exhausts.

Use adversaries for transport disruption and runtime failure only. Do not encode topology change, handoff, federation cutover, or mode change as adversaries.

Environment Extensions

Scenario.extensions is a domain-neutral namespace map for downstream environment config. Core telltale-simulator does not interpret those values directly. Instead a HostAdapter may inspect one namespace, build external environment models, and return them through HostAdapter::environment_models(...).

This is the supported path for downstream projects that need geometry, radio, mobility, or device-capability config without adding vertical-specific fields to core Scenario.

Reconfiguration Program

Scenario.reconfigurations is the first-class surface for simulator-visible topology and authority change. Each entry has the same trigger vocabulary as adversaries, but it activates a semantic reconfiguration operation instead of a transport disturbance.

#![allow(unused)]
fn main() {
pub struct ReconfigurationSpec {
    pub trigger: TriggerSpec,
    pub action: ReconfigurationAction,
    pub effect: ReconfigurationEffect,
}
}

Supported actions are:

  • link: update one directed link policy
  • delegation: record an explicit delegation scope transfer between two roles
  • handoff: record an explicit handoff identifier plus revoked/activated roles
  • federation: activate or clear a named communication grouping policy
  • mode_transition: record a mode change for one or more roles

effect.kind = "pure" records a pure reconfiguration and must not consume transition budget. effect.kind = "transition_choreography" records a separate cutover budget and must declare budget_cost > 0.

The simulator validates reconfiguration footprints before execution. Unknown roles, duplicated federation members, same-source/same-target delegation, empty handoff ids, and zero-cost transition choreography are rejected at parse time.

Network Middleware

NetworkModel wraps AdversaryInjector when network simulation is enabled. It applies federation connectivity checks, link overrides, latency sampling, loss sampling, and deferred delivery.

Per-link policies match directed (from, to) pairs. network.links now describes the static baseline policy for a run. Dynamic link updates happen through reconfigurations. Reconfiguration-applied link overrides shadow the baseline policy for the affected directed edge only.

Loss is evaluated before latency scheduling. Dropped messages never enter the in-flight queue. Zero effective latency produces immediate delivery.

The three middleware surfaces serve distinct purposes: network defines baseline transport parameters, reconfigurations handles topology and federation cutovers, and adversaries injects budgeted disturbances with theorem-facing assumption diagnostics.

Property Monitoring

PropertyMonitor performs online checks by scanning newly appended observable events and machine state each round. Built-in checks are:

  • NoFaults: verifies that no coroutine has entered a faulted state during execution.
  • Simplex: verifies that all coroutine state vectors remain on the probability simplex (all components non-negative, sum to 1 within tolerance).
  • SendRecvLiveness(sid, bound): verifies that every send in session sid is matched by a receive within bound session-local ticks. Uses per-session event counters rather than raw global ticks.
  • TypeMonotonicity(sid): verifies that session-type depth never increases for non-recursive local types in session sid. Recursive types are skipped.
  • BufferBound(sid, max): verifies that no buffer in session sid exceeds max pending messages.
  • Liveness(name, precondition, goal, bound): a custom liveness check. Once the precondition predicate becomes true, the goal predicate must become true within bound steps. Predicates can be no_faults, simplex, tick comparisons, or distance_to_equilibrium comparisons.

Invariant strings are parsed by parse_property. Predicate strings are parsed by parse_predicate.

Checkpointing and Replay

CheckpointStore snapshots protocol-machine state through the typed persisted replay contract at configured intervals. When checkpoint_interval is set, run_with_scenario(...) writes PersistedReplayArtifact checkpoint files under artifacts/<scenario.name>/. Replay loads one of those persisted artifacts and re-executes the same shared middleware loop used by fresh scenario runs. resume_with_checkpoint_artifact(...) is the exact resume path because it restores both protocol-machine state and simulator middleware state from the captured artifact.

Scenario replay artifacts also retain the resolved adversary schedule, budget-consumption history, assumption diagnostics, and the canonical reconfiguration trace.

Checkpoint snapshots currently require the canonical backend. Threaded scenario runs still emit replay artifacts but checkpoint serialization remains canonical-only, and scenarios that request both threaded execution and checkpoints fail validation instead of degrading later at run time. Checkpoint persistence is best-effort: serialization or file-write failures do not fail the run. CheckpointStore::last_persist_error() exposes the last recorded persistence error.

Current Limits

Generated effect-family helpers (e.g. record_return(), with_delay_ms(), record_stale_late_result()) are separate programmatic APIs. They are not a TOML scenario feature. They are also not yet wired into SimulationHarness.

Simulation Fields

This page documents the simulator’s field/environment-dynamics layer and the generic external environment boundary.

Field Layer

Telltale now treats field dynamics as one layer inside a broader environment model. The field layer is responsible for evolving shared fields, latent environment state, or node-local physical state when protocol-visible effects occur. It does not by itself decide topology, medium contention, mobility, or admission.

The simulator-facing Rust abstraction is FieldModel in rust/simulator/src/field.rs. A FieldModel supplies a stable layer name, an EffectHandler via build_handler(), and default per-role initial states via derive_initial_states(roles).

The built-in scenario schema uses the serde-tagged FieldSpec catalog through Scenario.field. Current built-in families are mean_field, hamiltonian, and continuum_field.

Adapter Integration

Use FieldAdapter::from_scenario(...) when built-in scenario field parameters should drive handler construction and default state derivation. Use FieldAdapter::new(...) or FieldAdapter::from_boxed_model(...) when a host integration wants the harness to own a custom FieldModel. The harness provides derive_initial_states(&Scenario) for the built-in schema and derive_initial_states_for_field_model(&dyn FieldModel, roles) for arbitrary implementations.

Simulator field handlers implement deterministic EffectHandler::step updates over fixed-point state. The runner stores field state in coroutine registers starting at register 2.

Initial State Derivation

derive_initial_states(&Scenario) builds default per-role state vectors from the built-in field when present. mean_field broadcasts one concentration vector to every role. hamiltonian maps each role index to [position, momentum]. continuum_field assigns one scalar field value per role.

If a HostAdapter returns explicit initial states, the simulator never consults the built-in field catalog.

Environment Boundary

External projects should extend the simulator through the domain-neutral environment hooks in rust/simulator/src/environment.rs. The shared execution core accepts five external model traits: TopologyModel for graph structure and reachability, MediumModel for link-level delay/loss/collision behavior, MobilityModel for node position updates, NodeCapabilityModel for per-node resource or radio constraints, and LinkAdmissionModel for connection-level admission decisions.

These hooks all receive the same EnvironmentSnapshot each round. That snapshot exposes current tick, logical step, participating roles, field-backed node state, node poses, node capabilities, and potential links. The shared execution core emits one canonical EnvironmentTrace of EnvironmentArtifact records for mobility updates, capability snapshots, reachability decisions, admission outcomes, and medium outcomes.

Extension Configuration

Scenario.extensions is the generic config hook for domain-owned environment configuration. It is a namespaced map of arbitrary TOML values so external projects can carry their own config without adding vertical-specific fields to core Scenario. Vertical-specific logic belongs in downstream crates, not in the core simulator schema.

Lean Mirror

Lean mirrors the executable field boundary under lean/Runtime/Simulation/Field.lean. That module includes a Lean-native FieldModel, FieldHandler, built-in FieldParams catalog, and default initial-state derivation for the shipped field families. It remains an executable parity layer rather than a mirror of Rust trait objects or serde-based scenario parsing.

Coverage

The main parity and extension lanes are:

  • rust/simulator/tests/field_handler_parity.rs
  • rust/simulator/tests/environment_models.rs
  • rust/simulator/tests/lean_reference_parity.rs
  • lean/Runtime/Tests/SimulatorParity.lean

environment_models.rs exists specifically to prove that topology, medium, mobility, capability, and admission hooks work through an external adapter without built-in domain knowledge.

Simulation Viewer

This page describes the typed model and service boundary for the simulator webapp work. It is intentionally about artifacts and application-service contracts first, with only the minimum crate-split notes needed to explain how telltale-ui and telltale-web consume it.

Scope

The simulator web stack is split into three layers:

  • telltale-viewer: pure artifact loading, schema/version handling, branch patch types, query/command models, and the application-service boundary
  • telltale-ui: the portable Dioxus UI core that consumes the telltale-viewer model layer
  • telltale-web: the thin WASM/browser shell around telltale-ui

telltale-viewer keeps browser APIs and renderer-local state out of the authoritative artifact boundary. telltale-ui and telltale-web are now present, but they are intentionally downstream of this crate split rather than new semantic authorities.

The viewer layer now also owns the typed command builders used by the graph/time-travel workspace. That keeps branch patch construction in telltale-viewer and lets telltale-ui stay an Observed command-emission shell instead of directly authoring patch details.

Artifact Families

The viewer is built around the simulator artifacts that already exist in telltale-simulator. The initial typed envelope supports these families:

  • scenario bundles
  • decision reports
  • environment traces
  • reconfiguration traces
  • sweep manifests
  • sweep diff reports
  • scheduler comparisons
  • observability comparisons
  • approximation manifests
  • contract-check reports
  • semantic comparison results
  • theorem-aware counterexamples
  • deterministic sweep reports
  • experiment-suite reports
  • effect-trace artifacts
  • minimization results

Each saved artifact uses one stable format marker and one explicit schema version so later UI layers can reject unsupported files cleanly.

Query and Command Split

The model layer uses an explicit command/query split.

Queries:

  • load artifact inventories
  • load one artifact
  • load scenario summaries
  • load branch-lineage projections
  • request graph projections
  • search loaded artifacts
  • select one historical inspection position
  • compare two artifacts semantically
  • find the first meaningful divergence between two artifacts
  • load theorem-aware counterexamples
  • load sweep explorer and experiment-suite reports
  • load typed effect traces
  • load deterministic minimization results
  • load downstream extension manifests

Commands:

  • import one artifact
  • create a branch
  • update a branch
  • delete a branch
  • request a rerun for a branch
  • execute a deterministic archived-artifact sweep
  • execute a baseline-vs-candidate experiment suite
  • request a mocked rerun with typed effect overrides
  • request deterministic minimization for a branch
  • register downstream extension manifests

This is deliberate. The UI should inspect authoritative artifacts through typed queries and emit typed branch/scenario patch commands rather than mutate run state directly.

ViewerReport is the first canonical report model built from that query surface. It lets the UI render artifact inventories and scenario summaries without opening downstream-specific assumptions in the Dioxus layer.

The newer reusable tooling surfaces build on that same boundary:

  • SemanticComparisonResult for exact, normalization-equivalent, and safety-visible divergence classification
  • TheoremAwareCounterexample for reusable theorem and divergence witnesses
  • DeterministicSweepReport and ExperimentSuiteReport for archived sweep and baseline/candidate execution families
  • EffectTraceArtifact and EffectOverrideSpec for effect inspection and mocked reruns
  • MinimizationRequest and MinimizationResult for deterministic witness reduction
  • ViewerExtensionManifest for downstream overlays, graph annotations, and time-travel extension panels

Branch Patches

Branch editing is patch-based. The model layer defines ScenarioBranchPatch and ScenarioPatchOperation instead of exposing direct mutable scenario state to the UI.

The current patch surface includes:

  • scenario metadata updates such as name, seed, and steps
  • execution policy updates
  • theorem-profile replacement
  • extension updates
  • reconfiguration replacement
  • full scenario replacement when the caller already owns a canonical scenario

Later phases can extend the patch language, but the UI should continue to emit typed deltas rather than mutate simulator state in place.

Comparison and Counterexamples

Semantic comparison is now a first-class viewer concern. The pure model layer exposes SemanticComparisonRequest, SemanticComparisonResult, and SemanticDivergencePoint. Those types deliberately sit above raw mismatch blobs:

  • exact raw match stays distinct from normalization-equivalent match
  • classification-only changes remain visible without being misreported as raw divergence
  • the first semantically meaningful divergence is queryable directly

TheoremAwareCounterexample composes with that comparison surface. It can be derived either from theorem-eligibility failure on one artifact or from a comparison that leaves the expected semantic regime. The viewer crate marks the helper constructors for these artifacts as authoritative sources so the browser shell does not mint them locally.

Sweeps, Effects, and Minimization

The pure model layer now also owns:

  • deterministic archived-artifact sweep execution through ExecuteSweep
  • baseline-vs-candidate experiment suites through ExecuteExperimentSuite
  • typed effect inspection via EffectTraceArtifact
  • typed mocked rerun requests via EffectOverrideSpec
  • deterministic branch minimization via MinimizationRequest

These are still command/query surfaces, not browser-owned mutation. The browser shell issues requests, and the application service owns the resulting artifacts and summaries.

Application Service

ViewerApplicationService is the stable boundary between the pure model layer and the future UI/web layers. The first implementation is InMemoryViewerService.

Even though the initial service is in-process, the boundary is already shaped like a real application service:

  • queries return typed model projections
  • commands mutate branch/workspace state explicitly
  • the browser shell does not become the semantic owner of simulation state

Ownership and Portability Notes

The shared viewer stack borrows Aura’s split and ownership discipline:

  • telltale-viewer remains pure and renderer-free
  • telltale-ui is an Observed surface over authoritative artifacts
  • telltale-web is the thin browser shell and owns Dioxus.toml, index.html, Tailwind packaging, and future browser-only APIs

The first shared ownership-marker set comes from telltale-macros:

  • #[observed_only]
  • #[actor_owned("...")]
  • #[authoritative_source("...")]
  • #[strong_reference("...")]
  • #[weak_identifier("...")]

These markers are intentionally lightweight. They validate the declared boundary shape and are backed by compile-fail tests and repo-level boundary checks.

Downstream Integration

Downstream projects should consume the shared viewer stack by importing the stable artifact and extension contracts instead of forking the shell. The supported downstream integration surface is now:

  • viewer artifacts and query/command types from telltale-viewer
  • extension manifests through ViewerExtensionManifest
  • extension slots for overview panels, graph annotations, time-travel panels, and insight panels
  • the existing ownership markers where they define public viewer/webapp boundaries, while narrower repo-local lint scripts remain internal

Simulation Viewer Webapp

This page describes the current shared Dioxus webapp surface for simulator inspection. It covers the crate split, global layout, graph and run-insight workspaces, ownership discipline, testing split, and local development workflow.

Crate Split

The viewer web stack is intentionally split into three crates:

  • telltale-viewer: pure artifact envelopes, report/query/command models, branch patching, and the application-service boundary
  • telltale-ui: portable Dioxus UI core with the shared shell, reusable components, graph rendering, route/view state, and task-owner helpers
  • telltale-web: thin browser shell with Dioxus.toml, index.html, Tailwind packaging, and future browser-only integration

Only telltale-web may own browser APIs. telltale-ui and telltale-viewer must stay portable.

App Ownership Model

The viewer is an Observed surface over authoritative simulator artifacts. It may shape presentation, selection, and browser-local layout state, but it must not become the semantic owner of simulation truth.

The current ownership markers used by the shared viewer stack are:

  • #[observed_only] for render/projection helpers
  • #[actor_owned("...")] for long-lived task/runtime helpers
  • #[authoritative_source("...")] for the narrow surfaces that publish authoritative demo or derived outputs
  • #[strong_reference("...")] and #[weak_identifier("...")] for typed reference/identifier boundaries

These are intentionally structural. The real semantic authority still lives in simulator artifacts and the application-service boundary.

Graph Workspace and Time Travel

The shared webapp now includes a graph workspace for deterministic structure and replay browsing. The graph page can switch between these projection families from the same authoritative artifact set:

  • choreography structure
  • instantiated protocol structure
  • execution timeline
  • branch lineage

Time travel is explicit. Users can:

  • step forward and backward through deterministic execution history
  • jump directly to a historical step
  • switch projections without losing typed selection state
  • search nodes, branches, and labels through the pure telltale-viewer index

Branch management is command-driven rather than mutation-driven. The graph workspace emits typed viewer commands for:

  • branch creation from the active historical step
  • branch update through a typed patch
  • branch deletion

Those commands are minted in telltale-viewer, while telltale-ui remains an Observed shell over the resulting command stream and graph projections.

Run Insight Workspace

The insight page is the companion debugging surface for one active run or branch. It currently renders:

  • execution regime and theorem scheduler profile
  • watch expressions over sampled semantic facts
  • branch provenance and narrative annotations
  • a run-diff summary for the active branch
  • semantic comparison classification and the first divergence summary
  • theorem-aware counterexamples
  • causality/explanation rows derived from the execution timeline projection
  • bookmarks for historical steps and branches
  • archive reload status for exact artifact-set re-entry
  • deterministic minimization summaries for reduced witnesses

This gives the viewer a first serious run-analysis lane instead of only a graph browser.

The archive controls are intentionally part of the insight workspace rather than a separate browser-owned workflow. Reloading an archived artifact set preserves the deterministic viewer state, and any subsequent branch fork still goes back through the typed command path instead of mutating the archived run in place.

Sweep and Effect Workspaces

The shared webapp now also exposes:

  • a sweep explorer page with filtering, outlier highlighting, and drill-down into the selected case
  • a suite page section for baseline-vs-candidate regression thresholds and counterexample presence
  • an effect page that renders typed effect traces and queued mocked rerun overrides

These pages still consume the same pure telltale-viewer query/command surface. The browser shell does not own sweep execution, suite comparison, effect mocking, or minimization semantics. It only drives the typed requests and renders the returned artifacts.

Downstream Handoff

Aura is unblocked to start integrating once it consumes these shared surfaces:

  • telltale_viewer::ViewerArtifactFile and telltale_viewer::ViewerArtifact as the stable artifact envelope
  • telltale_viewer::ViewerReport, ViewerQuery, ViewerCommand, and ViewerApplicationService as the query/command boundary
  • telltale_viewer::GraphProjection and GraphProjectionKind as the graph projection contract
  • telltale_ui::TelltaleUiRoot, ViewerFrame, ViewerPage, and ViewerWorkspace as the portable Dioxus shell and shared workspace model

The intended downstream extension points are:

  • artifact and insight overlays layered on top of the shared pages
  • graph annotations and domain-specific badges that do not replace the authoritative projection contract
  • downstream application-service implementations that feed the same query/command surface
  • extension manifests that target overview panels, graph annotations, time-travel panels, and insight panels without requiring a shell fork

Downstreams should not fork the core shell just to add overlays. The shared Telltale crates now provide the minimum handoff surface Aura needs.

Testing Split

The viewer work uses an explicit three-way testing split:

  • pure model tests in telltale-viewer
  • ownership/compile-fail/boundary checks in telltale-macros and just check-viewer-tooling-boundaries
  • Dioxus shell integration tests in telltale-ui

The canonical local/CI recipe is just check-viewer-tooling. That recipe runs the pure model tests, the UI shell tests, the thin web-shell compile, and the boundary check.

Local Development

For the current shared web shell:

cd rust/web
npm install
npm run tailwind:watch
dx serve --platform web

The Dioxus watcher is configured to watch:

  • rust/web/src
  • rust/ui/src
  • rust/web/styles
  • rust/web/public

The browser shell currently loads the demo workspace through the typed application-service path so the shared shell, graph, and insight surfaces are all exercised from one entrypoint. That same demo path now also exercises sweep exploration, effect inspection, mocked rerun commands, minimization summaries, and downstream extension slots.

Simulator CLI Note

Textual replay output remains available for compatibility and quick terminal checks, but it is now explicitly a compatibility summary path. The shared viewer is the preferred human-facing inspection surface for graph navigation, time travel, branch management, and run insights.

Session Graph Search

telltale-search implements ARA*-style weighted graph search over Telltale session graphs. It provides canonical search-machine semantics, deterministic batch extraction, proposal normalization, and replay artifacts. The crate is intentionally generic and free of simulator, viewer, browser, or application-specific routing dependencies.

Scope

The crate owns the search algorithm, its determinism contract, and the capability vocabulary for scheduler profiles, fairness assumptions, and observable classes. Downstream crates provide domain-specific node and edge semantics, heuristic and cost policies, graph snapshots, and epoch updates.

Execution policy is explicit and separate from downstream search-problem semantics. Downstream control policy may choose scheduler profile, batch width, caching mode, and effort regime, but that execution-side choice does not redefine the search objective or candidate meaning.

telltale-simulator can project search runs through project_search_run(...). telltale-viewer can project search artifacts through project_search_artifacts(...). These integration layers are optional and live above the crate boundary.

Allowed downstream variation includes:

  • node and edge meaning
  • heuristic and cost policy
  • success/selection criterion
  • reconstruction witness
  • execution profile

Non-goal: the crate does not define downstream domain truth, candidate eligibility, or objective semantics.

Core Surface

SearchDomain defines the contract a downstream domain must implement: successor generation, heuristic evaluation, snapshot identity, and optionally custom authority-surface derivation through the blanket SearchAuthorityPolicy hook. SearchCost and its concrete implementation EpsilonMilli define the cost algebra. EpsilonMilli uses fixed-point milli-precision with a u32 backing store.

SearchQuery generalizes the built-in problem surface:

  • SingleGoal for classic one-goal path search
  • MultiGoal for any-of-N terminal search
  • CandidateSet for selector-style best-candidate search

Release-facing callers should prefer the fail-closed constructors SearchQuery::try_multi_goal(...) and SearchQuery::try_candidate_set(...), which return SearchQueryError instead of panicking on empty accepted sets. The panicking convenience constructors remain available for internal happy-path call sites.

SearchDomain also owns the selected-result boundary explicitly:

  • accepts_terminal(...) for the built-in query adapters
  • selected_result_candidates(...) when admissibility or winner eligibility depends on discovered machine state
  • selected_result_semantics_class(...) to declare whether selected-result semantics are query-derived or domain-defined from discovered state
  • reconstruct_selection_witness(...) and compare_selected_solutions(...) for optional witness/export ordering

SearchMachine owns the frontier, parent map, selected solution, and budget state. The historical incumbent vocabulary is still present for compatibility, but the crate also exports selected-result aliases and accessors. SearchMachine exposes step_once(...) for single canonical steps and explicit invariant checks for partition discipline, parent-score coherence, selected-result coherence, and batch legality. CanonicalBatch and Proposal represent the min-key batch window and its normalized expansion results. The built-in path-search adapters still reconstruct canonical paths.

Runtime Surface

run_with_executor(...) is the canonical host execution entry point. SearchExecutionPolicy is the explicit execution-side runtime policy surface: it carries scheduler profile, batch width, caching profile, and effort profile. SearchRunConfig packages that execution policy together with the declared fairness assumptions. validate_run_config(...) enforces fail-closed checks for unsupported execution-policy variants, executor-kind mismatches, fairness mismatches, and batch-width mismatches before execution begins. The executor strategy is supplied separately via the ProposalExecutor implementation and does not define search-problem semantics.

Current execution-policy status:

  • stable and implemented: EphemeralPerStep caching and RunToCompletion
  • stable and implemented with a weaker result contract: SchedulerStepBudget(n), where one budget unit is one full canonical batch/service step and runs stop only at batch barriers
  • explicitly rejected and outside the stable import posture: IncrementalReuse

The supported execution-policy matrix is:

Scheduler profileExecutor kindBatch widthRequired fairnessApproximation contract
CanonicalSerialserial1DeterministicSchedulerConfluenceExact
ThreadedExactSingleLanenative parallel1DeterministicSchedulerConfluenceExact
BatchedParallelExactnative parallel> 1DeterministicSchedulerConfluenceCertifiedWindowExact
BatchedParallelEnvelopeBoundednative parallel> 1EventualLiveBatchService, NoStarvationWithinLegalWindowEnvelopeBounded

SchedulerStepBudget(n) overlays that matrix as a stable effort mode and changes the result contract to BudgetedAnytime. IncrementalReuse remains outside the stable import posture and is rejected fail-closed.

ProposalExecutor is the trait for batch expansion strategies. SerialProposalExecutor runs proposals sequentially. NativeParallelExecutor uses rayon when the multi-thread feature is enabled. Constructing NativeParallelExecutor without the feature fails explicitly rather than silently degrading.

The runtime emits SchedulerArtifact for scheduler classification and the declared execution policy, SearchFairnessArtifact for profile-scoped fairness evidence, SearchResultBoundArtifact for generic selected-result discovery and quality reporting, with SearchRouteBoundArtifact retained as the path-search compatibility alias, and packages them together with state traces and progress summary in SearchExecutionReport. SearchStateArtifact provides per-round state and certificate traces for exact-profile correspondence checks. SearchFullStateArtifact is the Rust-facing extraction boundary for the full-machine refinement lane: it includes OPEN, CLOSED, INCONS, g_score, parent, incumbent, epsilon, epoch/snapshot, last-batch nodes, and full commit/publication traces. SearchFairnessArtifact is the proof-claim carrier: it records the profile claim class, theorem-side certificate, and whether exact commit-trace/state-slice/observation equivalence to canonical serial is part of the claimed surface for that profile.

Generic selected-result bounds no longer carry a distinguished goal anchor. Optional path-goal discovery helpers are now split out under SearchPathProblemDiscoveryArtifact (route_bounds.path_problem), which is present only for built-in single-goal path queries.

SearchResultSummary is the generic selected-result summary exported in result bounds. Path-search-specific summary data now lives under its optional path_summary helper, with SearchRouteSummary retained as the compatibility alias for that path-specific wrapper rather than the primary generic summary. The route-oriented compatibility aliases are intentionally narrowed to telltale_search::compat (and telltale_search::runtime::compat for runtime-only imports).

The theorem-pack surface is also classified conceptually into:

  • generic machine/refinement/fairness theorems
  • generic selected-result/result-bound theorems
  • path-problem-specific completeness/discovery theorems

Rust currently exposes helper classification for this split through classify_theorem_problem_class(...) and theorem_inventory_problem_classes(). The theorem-pack artifact now exports that split directly in inventory_problem_classes alongside inventory_support_classes. Lean mirrors the same distinction in the search theorem inventory docs.

Admission and Capabilities

SearchDUser and SearchCertifiedCapability define the admission vocabulary. check_capability_containment(...) verifies that a user’s certified capabilities satisfy the requirements of a given theorem/claim class, scheduler profile, fairness assumption set, and observable class list. The check is fail-closed and returns structured AdmissionRejectionReason values on mismatch.

The capability vocabulary covers five dimensions: SearchClaimClass, SearchSchedulerProfile, SearchFairnessAssumption, SearchDeterminismMode, and SearchObservableClass. These are first-class observable artifacts and comparison inputs throughout the runtime and replay surfaces.

Replay and Epoch Reconfiguration

Replay semantics are fail-closed. SearchReplayArtifact captures the canonical round commits, epoch schedule, snapshot schedule, phase schedule, and batch schedule from a prior run. replay_observation(...) re-derives the final observation from canonical round commits and rejects drift against the stored artifact when selected-result semantics are query-derived. For domains that declare DomainDefinedFromDiscoveredState selected-result semantics, replay fails closed with UnsupportedSelectedResultSemantics instead of silently falling back to any compatibility goal anchor.

EpochReconfigurationRequest and commit_epoch_reconfiguration(...) handle graph-epoch transitions. Reconfiguration now carries an explicit SearchReseedingPolicy:

  • StartOnly resets the frontier and reseeds only from the canonical start
  • PreserveOpenAndIncons preserves the live frontier plus the retained parent closure needed for witness reconstruction

Replay and state artifacts record the applied reseeding policy on runs that cross epoch barriers. Runtime failures during proposal generation do not consume the canonical batch.

Observation and Comparison

SearchObservationArtifact captures the observable surface of a completed search run. compare_observations(...) compares two artifacts against a determinism mode and observable class list. Under ModuloCommutativity mode, normalized commit traces are compared as multisets rather than sequences.

Observable classes still use the historical incumbent/path terms for backward compatibility on deserialization, but the primary exported names are now the generic selected-result terms. Observable classes include SelectedResultCost, SelectedResultWitnessIdentity, SelectedResultPublicationTrace, NormalizedCommitTrace, GraphEpochTrace, SchedulerProfileTrace, and ProgressAccounting.

Profiling Surface

The repo now includes an explicit profiling matrix for the generalized search surface:

  • end-to-end Criterion workloads in rust/search/benches/search_profiles.rs
  • phase microbenchmarks in rust/search/benches/search_machine_phases.rs
  • runtime-overhead microbenchmarks in rust/search/benches/search_runtime_overheads.rs
  • structural counters in rust/search/benches/support.rs
  • saved-profile helpers in scripts/ops/profile-search-bench.sh
  • convenience entry points:
    • just profile-search-chain
    • just profile-search-rebuild
    • just profile-search-threaded
    • just profile-search-machine-only
    • just profile-search-artifact
    • just profile-search-invariants

The benchmark harness reports frontier growth, batch count, proposal churn, duplicate elimination, commit count, rebuild count, and publication count so performance work can separate generic machine cost from executor and artifact overhead. The profiling surface should be used before making search-structure optimizations, and hotspot assumptions should not be treated as stable until after the generalized query/result/authority surfaces are in place.

The profiling split is intentionally layered:

  • algorithm/search work: search_profiles and the machine-only runtime-overhead bench
  • executor/scheduler overhead: compare runtime_machine_only_chain_256 against runtime_executor_serial_chain_256
  • theorem/replay/artifact work: runtime_observation_export_chain_256 and runtime_full_state_export_chain_256
  • invariant-checking cost: runtime_invariant_check_frontier_512

Current review findings:

  • replacing next_batch(...) full-frontier sorting with a naive two-pass min-score scan regressed both phase_next_batch_frontier_512 and the end-to-end serial_chain_256 workload, so that change was rejected
  • duplicate-heavy and rebuild-heavy workloads still show normalization churn and rebuild work as stronger optimization candidates than batch extraction
  • normalize_proposals(...) was then changed from full sort-plus-dedup to per-target best-selection after the duplicate-pressure phase bench isolated that stage as the stronger hotspot
  • on phase_normalize_proposals_duplicate_pressure_64x32, that change reduced the measured phase time from roughly 120.75-132.44 µs to 44.59-56.46 µs
  • the improved workload class is generic machine normalization cost on duplicate-heavy frontiers, and it did not require theorem or artifact-schema changes
  • any further optimization should be justified against those measured workload classes, not assumed from the frontier implementation alone

The check-search-bench-tooling just target is the regression guard for this surface. It syntax-checks the saved-profile script and compiles the search bench targets in both default and multi-thread configurations without running them. just check-search-tooling now calls it, so the benchmark and saved-profile surface is part of the normal search verification lane rather than an optional local-only check.

Lean Proof Support

The Lean formalization lives in Runtime.Proofs.Search. The public proof surface is now split explicitly into a curated generic-machine barrel (Generic) and the path-problem-specific completeness/publication family (PathProblems), both re-exported through API. The supporting modules build from frontier vocabulary through one-step fairness and artifact refinement into full-machine semantics, liveness, completeness, and approximation-contract inventory rows.

The theorem inventory now mirrors the Rust three-way claim split:

  • generic_machine
  • generic_selected_result
  • path_problem_specific

The per-profile fairness claim taxonomy determines what the runtime can assert on behalf of each scheduler profile:

ProfileFairnessClaimClassProof basis
canonicalSerialexactOneStepproved unconditionally
threadedExactSingleLaneexactOneStepUnderRefinementproved via merge-refinement to canonical (any lane width, any lane completion order)
batchedParallelExactpremisedWindowBoundedproved under BatchedExactWindowCertificate
batchedParallelEnvelopeBoundedpremisedWindowBoundedproved under BatchedEnvelopeWindowCertificate

The batched profiles carry certified-window fairness, not end-to-end replay equivalence. What each profile claims is exactly what Lean justifies.

Proof Vocabulary (Core)

Runtime.Proofs.Search.Core establishes the foundational vocabulary:

  • FrontierEntry - a (node : Nat, priority : Nat) pair, the unit of frontier membership.
  • IsMinPriority frontier entry - entry is present in frontier and no other entry has strictly lower priority.
  • canonicalBatch frontier - the list of all entries satisfying IsMinPriority. These are exactly the entries serviced in the next step.
  • frontierAfterCanonicalStep frontier - the frontier with the canonical batch removed.
  • OneStepFair frontier - every IsMinPriority entry is absent from frontierAfterCanonicalStep frontier.
  • CanonicalSerialTrace - a dynamic frontier trace whose successive states are related by frontierAfterCanonicalStep.
  • EventuallyServicedWithin trace start bound entry - entry is removed from trace start within bound steps.
  • ContinuouslyEligible, NoStrictlyBetterEntryAppears - predicates used in the dynamic liveness theorem to constrain how the frontier evolves.

The reduced artifact structures StepArtifact, StateSlice, ObservationSlice, and StateArtifactSchema define the boundary at which Lean and Rust search states are compared. BatchedExactWindowCertificate is a proof-carrying record whose fields provide evidence that a batched window covers the current canonical batch and produces a commit for each covered entry. Downstream fairness theorems are conditioned on holding one of these certificates.

Fairness Proofs (Fairness)

Runtime.Proofs.Search.Fairness proves unconditional one-step fairness for canonicalSerial at the current min-key batch. Broader eventual-service results for non-min entries live in Liveness under explicit premise bundles, there is no unconditional all-entry fairness theorem in this module.

  • mem_canonicalBatch_iff_isMinPriority - membership in the canonical batch is equivalent to satisfying IsMinPriority. This connects the computational filter to the logical predicate.
  • canonical_batch_entries_removed_after_step - every entry in the canonical batch is absent after the step. This is the core removal fact.
  • canonical_serial_one_step_fair_for_min_priority_entries - if entry satisfies IsMinPriority, it is absent after one canonical step. This is the main one-step fairness result.
  • canonical_serial_batch_is_one_step_fair - the full frontier is OneStepFair.
  • currently_min_priority_eventually_serviced_within_one_step - in any CanonicalSerialTrace, a current IsMinPriority entry satisfies EventuallyServicedWithin ... 1. The service bound is exactly one step.
  • continuously_eligible_without_strictly_better_entries_eventually_serviced - the dynamic liveness result: if an entry remains eligible and no strictly better entry arrives across a horizon, it is serviced within one step.

Refinement Proofs (Refinement)

Runtime.Proofs.Search.Threaded models the threaded step independently of the canonical step, following NativeParallelExecutor::generate: the frozen canonical batch is tagged with batch indices, split into contiguous lanes of non-zero width (laneChunks, mirroring par_chunks), each lane computes its commits independently, lanes may complete in any order (a LaneSchedule carries an arbitrary permutation of the lanes), and results are merged deterministically by batch index. threadedMergedResults_eq_canonical proves the merged result equals the canonical indexed batch for every width and completion order. threaded_step_artifact_eq_canonical lifts this to the step artifact. The model does not cover successor proposals or authority conflicts, which the reduced artifact does not carry.

Runtime.Proofs.Search.Refinement then derives equality at every reduced artifact boundary, each quantified over the LaneSchedule:

  • threaded_exact_single_lane_step_artifact_refines_canonical - the full StepArtifact is equal.
  • threaded_exact_single_lane_commit_trace_refines_canonical and threaded_exact_single_lane_batch_nodes_refine_canonical - the normalizedCommits and batchNodes fields match individually.
  • threaded_exact_single_lane_state_slice_refines_canonical and threaded_exact_single_lane_observation_slice_refines_canonical - the reduced StateSlice and ObservationSlice projections are equal.
  • threaded_exact_single_lane_multi_step_state_trace_refines_canonical, ..._observation_trace_..._canonical, and ..._state_artifact_trace_..._canonical - all three multi-step trace functions agree across arbitrary FrontierTrace inputs and per-step lane schedules.

Executable Semantics (Executable)

Runtime.Proofs.Search.Executable is the semantic center of the search proof lane: an executable reduced machine whose transitions and artifact projections are all explicitly proved.

  • SearchMachineState - reduced proof-relevant machine state carrying frontier, closed nodes, inconsistent nodes, score table, parent witnesses, incumbent, phase, and progress accounting.
  • MachineInvariants - reduced machine invariant bundle covering open/closed disjointness, incons ⊆ closed, parent-score coherence, incumbent coherence, and nodup conditions.
  • executableCanonicalStep - concrete reduced step function implementing canonical batch extraction, batch removal, normalized commit production, open/closed/incons movement, score updates, and reduced incumbent refresh.
  • stateSliceOfMachineState, observationSliceOfMachineState, and stateArtifactOfMachineState - explicit Rust-facing reduced extraction functions from executable machine states into the reduced artifact vocabulary.

Key theorems:

  • executable_canonical_step_preserves_invariants - executable reduced stepping preserves the reduced machine invariant bundle.
  • executable_step_artifact_refines_canonical_step_artifact - the executable step refines exactly to the existing reduced step-artifact vocabulary.
  • runtime_state_artifact_of_machine_state_is_projection - the reduced runtime-state artifact is an explicit projection of executable machine state.

Machine Vocabulary (Machine)

Runtime.Proofs.Search.Machine packages the executable semantics into the machine-facing theorem surface.

  • CanonicalMachineStep - packaged executable reduced step plus current-state invariant bundle.
  • CanonicalMachineTrace - machine trace version of canonical serial search.

Key theorems:

  • canonical_machine_step_services_current_min_priority_entries - the machine-level step relation preserves the same one-step service fact as the frontier-only fairness layer.
  • canonical_machine_step_preserves_invariants - machine invariants are preserved by the executable reduced step.
  • canonical_machine_trace_currently_min_priority_eventually_serviced - machine traces inherit the frontier-facing one-step eventual-service theorem through frontier projection.
  • executable_trace_refines_canonical_machine_trace - executable machine traces refine directly into the packaged machine-step theorem surface.
  • canonical_machine_step_artifact_refines_runtime_boundary and canonical_machine_state_artifact_is_runtime_projection - explicit refinement back into the Rust-facing reduced artifact vocabulary.

Full-Machine Semantics (FullMachine)

Runtime.Proofs.Search.FullMachine lifts the search lane above the reduced frontier model into an executable proof-side machine that mirrors the proof-relevant Rust state more closely.

  • FullSearchMachineState - executable full-machine state carrying OPEN, CLOSED, INCONS, g_score, parent table, incumbent, epsilon, phase, epoch, snapshot, last batch, and commit/publication traces.
  • fullStepOnce, fullDecreaseEpsilonAndRebuild, and fullCommitEpochReconfiguration - executable full-machine transitions for canonical stepping, rebuild, and epoch changes.
  • FullMachineInvariants - full-machine invariant bundle covering open/closed discipline, incons ⊆ closed, parent-score coherence, publication coherence, incumbent coherence, and nodup conditions.
  • FullStateArtifactSchema and fullStateArtifactOfFullState - explicit full-state artifact projection aligned with the Rust-side SearchFullStateArtifact export.

Key theorems:

  • full_state_artifact_of_full_state_is_runtime_projection - the exported full-state artifact is exactly the projection of the Lean full-machine state.
  • reduced_state_of_full_state_preserves_machine_invariants - the richer full-machine invariant bundle implies the existing reduced machine invariants.
  • full_activate_batch_preserves_invariants, full_apply_proposal_preserves_invariants, full_commit_proposals_preserves_invariants, full_decrease_epsilon_and_rebuild_preserves_invariants, full_commit_epoch_reconfiguration_preserves_invariants, and full_step_once_preserves_invariants - the full-machine transition families have explicit invariant-preservation theorems.
  • full_activate_batch_refines_reduced_service_window and full_step_once_refines_reduced_executable_step - the richer machine semantics refine back into the reduced executable step surface.

Liveness Proofs (Liveness)

Runtime.Proofs.Search.Liveness extends the search lane beyond the current min-key batch. All results are premise-scoped: termination is proved only for the named premise bundles in the theorem statements, and completeness assumptions are made explicit in the theorem premises rather than left as documentation-only caveats.

Termination:

  • closed_nodes_length_le_reachable_length - under the fixed-phase premise bundle, closed-node cardinality stays bounded by a finite reachable-node universe.
  • fixed_phase_canonical_serial_terminates_under_finite_reachable_bound - fixed-phase canonical serial search terminates under an explicit finite reachable-node premise bundle.
  • rebuild_aware_canonical_serial_terminates_under_phase_work_measure - rebuild-aware ARA-style termination under a lexicographic phase/work progress measure encoded into a natural descent argument.

Non-min fairness:

  • bounded_strict_preemption_eventually_becomes_min - a continuously present non-min-priority entry eventually reaches the min-key window when the count of strictly better entries is bounded and decreases strictly while it remains non-min.
  • canonical_serial_nonmin_entry_eventually_serviced_under_bounded_strict_preemption
    • composes the previous theorem with one-step fairness to obtain a service bound for non-min entries.
  • finite_better_entry_exhaustion_eventually_becomes_min and canonical_serial_nonmin_entry_eventually_serviced_under_finite_better_entry_exhaustion
    • stronger variants using a finite better-entry universe rather than a numeric preemption bound.
  • canonical_serial_nonmin_entry_eventually_serviced_under_scheduler_fairness - public scheduler-facing non-min fairness theorem. Its only premises are that the trace follows the canonical serial scheduler and the entry is present. The service horizon (number of strictly better competitors at the start) is computed, and the strict drain of better work is proved (strictlyBetterEntryCount_canonical_step_lt), not assumed. The reduced frontier model has no new arrivals, so preemption is automatically bounded.

Completeness:

  • canonical_serial_goal_reached_from_ready_witness_path - from an explicit ready witness path whose successive frontier entries become service-ready in order, the goal entry is eventually serviced.
  • canonical_machine_goal_reached_from_ready_witness_path - machine-level version: executable canonical stepping closes the goal node within the witness-path horizon.
  • canonical_machine_goal_reached_from_graph_reachability - completeness driven by an explicit graph-reachability premise bundle naming a reachable-node path and its ready entry path.
  • canonical_machine_goal_reached_from_raw_successor_semantics - public completeness theorem phrased over the raw successor contract, with ready-entry progress internalized behind an explicit refinement premise.
  • goal_reachability_connects_to_incumbent_publication - machine-level bridge from bounded goal reachability to incumbent publication under an explicit publication premise bundle.
  • eventual_optimal_goal_publication_under_admissible_consistent_heuristic (AStarOptimality) - A*-style optimality. HeuristicConsistent (h n ≤ c(n,n') + h n') and HeuristicAdmissible (h n ≤ cost of any path to the goal) are concrete. Admissibility is proved from consistency and h goal = 0. Optimal cost of the published incumbent is proved from the machine’s min-priority expansion order, g-score soundness of goal entries, and optimalCost being a lower bound on source-to-goal path costs. The standard A* frontier invariant (before publication some frontier entry lies on an optimal path with exact g) remains an explicit premise, because the reduced machine does not model successor expansion.

Envelope Surface (Envelope)

Runtime.Proofs.Search.Envelope carries the certified-window theorem surface for batchedParallelEnvelopeBounded. It provides the same certificate structure as batchedParallelExact but scoped to the envelope-bounded service window.

  • BatchedEnvelopeWindowCertificate - theorem-side certificate object for one legal envelope-bounded service window.
  • certified_batched_envelope_window_is_fair - every current IsMinPriority entry is removed within the certified envelope window.
  • certified_batched_envelope_window_eventually_services_min_priority_entries
    • restates the one-window fairness theorem as EventuallyServicedWithin ... 1.
  • certified_envelope_window_trace_is_valid - validates exported envelope certificate traces against the legal current min-key batch.

Profile Claims (ProfileClaims)

Runtime.Proofs.Search.ProfileClaims derives the per-profile theorem surface from the upstream proof modules, indexing each result by SearchSchedulerProfile and FairnessClaimClass. The claim table at the top of this section shows what each profile carries.

  • canonical_serial_profile_has_exact_one_step_fairness - pairs the exactOneStep claim tag with OneStepFair frontier for every frozen frontier (from canonical_serial_batch_is_one_step_fair).
  • canonical_serial_goal_window_service_has_exact_suffix_bound - every min-priority entry is gone after one canonical step.
  • threaded_exact_single_lane_has_exact_one_step_fairness - derives one-step fairness for the threaded profile by composing the Refinement equalities.
  • threaded_exact_single_lane_has_exact_observation_equivalence - observation slices of the lane-partitioned, merge-normalized threaded step match canonical serial exactly for every LaneSchedule.
  • certified_batched_exact_window_is_fair - given a BatchedExactWindowCertificate, every IsMinPriority entry is removed within the certified window.
  • certified_batched_exact_window_eventually_services_min_priority_entries - restates the above as an EventuallyServicedWithin bound of 1.
  • certified_batched_exact_window_bounded_dynamic_starvation_freedom - given certificates for each step in a horizon, a ContinuouslyEligible entry under BoundedPreemptionWindow is serviced within bound steps.
  • certified_window_trace_is_valid_for_exact_batch_service - validates that a certificate sequence matches the canonical batch at every step with a service bound of 1.
  • batched_parallel_envelope_claim_is_premised - states formally that batchedParallelEnvelopeBounded carries the theorem-backed certified-window claim class.

Theorem Inventory (Inventory and TheoremPack)

Runtime.Proofs.Search.Inventory records every theorem by name together with a Bool indicating whether it is proved. Runtime.Proofs.Search.TheoremPack exports a support classification for every row: executableSemantics, refinementCorollary, or premiseScoped.

The inventory has 50 proved theorems and 0 unproved. Coverage includes the full-machine artifact and projection surface, full-step refinement contracts back into the reduced executable lane, a raw-successor completeness theorem surface, scheduler-fair non-min service, and the certified-window envelope profile theorems.

Runtime.Proofs.Search.TheoremPack wraps the inventory, support classes, and two numeric bounds into a SearchFairnessTheoremPack structure. The mirrored Rust SearchTheoremPackArtifact carries those same rows plus the release-gate name:

inventorySupportClasses
canonicalServiceBoundSteps                 := 1
canonicalGoalWindowDiscoverySuffixBoundSteps := 1

Both bounds are exactly one step, matching the EventuallyServicedWithin ... 1 statements in Fairness and ProfileClaims. The dedicated local verification gate is just check-search-fairness. The theorem-pack is exported through the runtime API and written to target/search-theorem-pack/search-theorem-pack.json for release and provenance checks. On the Rust side this appears in SearchTheoremPackArtifact as both inventory and inventory_support_classes, so downstream checks can distinguish executable semantics theorems from refinement corollaries and premise-scoped theorems.

Artifact Vectors

Stable source-controlled search artifact vectors are exported through cargo run -p telltale-search --example export_vectors and checked by the search_vectors conformance test. This covers theorem-pack, final-state, fairness, and replay artifact drift.

A separate recovery vector surface is exported through cargo run -p telltale-search --example export_recovery_vectors and checked by search_recovery_vectors. This covers epoch-transition recovery bounds and route summaries.

The release and provenance lane records both the theorem-pack JSON and the generated canonical vector artifact at target/search-artifacts/search-vectors-v1.json, plus the recovery vector artifact at target/search-artifacts/search-recovery-vectors-v1.json.

Target Support

The core serial machine is target-agnostic. WASM builds use the same canonical serial and replay surface without rayon. The optional multi-thread feature enables the native parallel executor for native targets.

Lean/Rust Parity

Reduced Lean/Rust parity covers canonical batch-window extraction, proposal independence over declared authority surfaces, theorem-pack support-class parity, the richer Rust full-state artifact boundary, and epoch-barrier semantics with fairness-bundle fixtures. See Rust-Lean Bridge and Parity for the deviation registry and parity policy.

Migration Note

For downstream selector-style imports such as Jacquard:

  • prefer SearchQuery::try_multi_goal(...) and SearchQuery::try_candidate_set(...) over panicking query builders
  • prefer SelectedSolution, SearchResultBoundArtifact, and SearchResultSummary
  • use SearchDomain::selected_result_candidates(...) when winner eligibility depends on discovered machine state
  • consume SearchClaimClass and inventory_problem_classes rather than scraping theorem names
  • avoid the route/incumbent compatibility aliases unless migrating legacy code, because the intended compatibility surface is telltale_search::compat
  • treat IncrementalReuse as out of the stable import posture until it is implemented

Content Addressing

Content addressing assigns stable hashes to protocol artifacts. The hash is computed from a canonical serialization, which enables memoization and structural sharing.

ContentId and Hasher

ContentId wraps a hash digest produced by a Hasher.

#![allow(unused)]
fn main() {
pub trait Hasher: Clone + Default + PartialEq + Send + Sync + 'static {
    type Digest: AsRef<[u8]> + Clone + PartialEq + Eq + Hash + Send + Sync + 'static;
    const HASH_SIZE: usize;
    fn digest(data: &[u8]) -> Self::Digest;
    fn algorithm_name() -> &'static str;
}

pub type DefaultContentHasher = Blake3Hasher;

pub struct ContentId<H: Hasher = DefaultContentHasher> {
    hash: H::Digest,
}
}

DefaultContentHasher is the central policy alias for content addressing and currently resolves to Blake3Hasher. SHA-256 remains available as an explicit alternative behind the sha256 crate feature when compatibility matters. A custom Hasher implementation can still trade off speed or proof system constraints.

The runtime heap reuses the same Hasher abstraction, but it does not share the same byte-level encoding contract. Heap resources use the runtime-local canonical format described in Resource Heap. That separation is intentional.

Contentable

Types that can be serialized canonically implement Contentable.

#![allow(unused)]
fn main() {
pub trait Contentable: Sized {
    fn to_bytes(&self) -> Result<Vec<u8>, ContentableError>;
    fn from_bytes(bytes: &[u8]) -> Result<Self, ContentableError>;
    fn from_bytes_verified<H: Hasher>(
        bytes: &[u8],
        expected: &ContentId<H>,
    ) -> Result<Self, ContentableError>;
    fn to_template_bytes(&self) -> Result<Vec<u8>, ContentableError>;
}
}

GlobalType, LocalTypeR, Label, and PayloadSort implement this trait. The serializer converts types to a de Bruijn representation and normalizes branch ordering. Alpha equivalent types share the same content ID. Callers loading bytes from a content-addressed store should prefer from_bytes_verified, which hashes the bytes first and rejects a content-ID mismatch before deserializing.

Closed vs Open Terms

content_id is defined for closed terms only. Open terms use template_id, which includes an explicit free-variable interface in the serialized template envelope.

#![allow(unused)]
fn main() {
use telltale_types::{GlobalType, Label};
use telltale_types::contentable::Contentable;

let open = GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("free_t"));
assert!(open.content_id_default().is_err());

let template_id = open.template_id_default()?;
let template_bytes = open.to_template_bytes()?;
}

Template bytes and template IDs enable caching and comparing partially specified protocols before all binders are resolved.

Serialization Formats

JSON is the default format. DAG-CBOR is available with the dag-cbor feature and produces compact binary output.

#![allow(unused)]
fn main() {
use telltale_types::{GlobalType, Label};
use telltale_types::contentable::Contentable;

let g = GlobalType::send("A", "B", Label::new("msg"), GlobalType::End);
let json_bytes = g.to_bytes()?;
let cid = g.content_id_default()?;
}

JSON and DAG-CBOR produce different content IDs for the same value. One format should be used consistently within a system. Switching formats is a cache-boundary event that requires regenerating persisted IDs.

ContentStore

ContentStore maps content IDs to cached values and tracks cache metrics.

#![allow(unused)]
fn main() {
use telltale_types::content_store::ContentStore;
use telltale_types::{GlobalType, Label, LocalTypeR};

let mut store: ContentStore<GlobalType, LocalTypeR> = ContentStore::new();
let global = GlobalType::send("A", "B", Label::new("msg"), GlobalType::End);
let local = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::End);

store.insert(&global, local.clone())?;
let cached = store.get(&global)?;
}

KeyedContentStore adds an additional key alongside the content ID for composite lookups.

Memoization Pattern

Content IDs are useful for caching projection results.

#![allow(unused)]
fn main() {
use std::collections::HashMap;
use telltale_types::{ContentId, GlobalType, Label, LocalTypeR};
use telltale_theory::project;

let global = GlobalType::send("Alice", "Bob", Label::new("ping"), GlobalType::End);
let mut cache: HashMap<(ContentId<_>, String), LocalTypeR> = HashMap::new();
let cid = global.content_id_default()?;
let key = (cid, "Alice".to_string());

if let Some(cached) = cache.get(&key) {
    // use cached
} else {
    let local = project(&global, "Alice")?;
    cache.insert(key, local);
}
}

This avoids repeated projection work when the same global type appears multiple times.

See Choreographic Projection Patterns for the projection pipeline and Resource Heap for higher-level storage patterns.

Resource Heap

The resource heap provides explicit runtime state tracking for choreography-layer resources. It stores resources under deterministic identifiers, tracks consumed resources in a nullifier set, and can derive Merkle commitments for the current heap state. This module lives in telltale_runtime::heap.

Scope

The heap is a telltale-runtime utility. It is not the same system as the type-level content addressing in telltale-types. The runtime heap uses the shared Hasher abstraction with its own canonical heap encoding and commitment logic.

The heap now has a focused Lean parity mirror. lean/Runtime/Resources/HeapModel.lean mirrors canonical bytes, tagged preimages, ordering rules, proof-index semantics, and deterministic replay for the published heap corpus. Digest computation remains Rust-side and is checked against published vectors rather than reimplemented in Lean.

Resource Identifiers

Resources are identified by ResourceId. Each ID stores a hasher digest and an allocation counter. The current default uses the shared BLAKE3 policy through DefaultContentHasher. The identifier is computed by hashing the canonical resource bytes plus the allocation counter.

#![allow(unused)]
fn main() {
pub struct ResourceId<H: Hasher = DefaultContentHasher> {
    hash: H::Digest,
    counter: u64,
}
}

This means the identifier is allocation-unique rather than pure-content-stable. Two identical resources allocated with different counters produce different ResourceId values. That behavior is intentional in the current runtime heap.

Resource Types

The heap stores four resource kinds.

#![allow(unused)]
fn main() {
pub enum Resource {
    Channel(ChannelState),
    Message(Message),
    Session { role: String, type_hash: u64 },
    Value { tag: String, data: Vec<u8> },
}
}

ChannelState stores sender, receiver, and an in-memory queue of MessagePayload. Message stores source, destination, label, payload bytes, and a sequence number. Session stores a role name and a type_hash value.

Heap Structure

The heap uses deterministic ordered containers.

#![allow(unused)]
fn main() {
pub struct Heap<H: Hasher = DefaultContentHasher> {
    resources: BTreeMap<ResourceId<H>, Resource>,
    nullifiers: BTreeSet<ResourceId<H>>,
    counter: u64,
}
}

BTreeMap and BTreeSet give stable iteration order. That stable order is important for reproducible commitments and proofs. The nullifier set records consumed resource IDs without deleting them from the resource map.

Heap APIs

The heap supports both functional and mutable update styles. The functional APIs return a new heap value. The mutable APIs exist for efficient in-place construction and updates.

#![allow(unused)]
fn main() {
use telltale_runtime::heap::{DefaultHeapHasher, Heap, Resource};

let heap = Heap::<DefaultHeapHasher>::new();
let msg = Resource::message("Alice", "Bob", "Ping", vec![1, 2, 3], 0);
let (msg_id, heap) = heap.alloc(msg);
let heap = heap.consume(&msg_id)?;
}

alloc(...), consume(...), remove(...), alloc_many(...), and consume_many(...) are the main functional operations. alloc_mut(...) and consume_mut(...) provide mutable variants. consume_many(...) validates the whole batch first and then nullifies all requested resources atomically.

read(...) only succeeds for active resources. If a resource has been consumed, read(...) returns HeapError::AlreadyConsumed. Use contains(...), is_consumed(...), is_active(...), active_resources(...), and consumed_ids(...) when you need explicit state inspection.

Size and Retention Semantics

size() returns the number of entries in the resource map. Consumed resources still count toward that number because consume(...) keeps them in the heap and only adds a nullifier. nullified_count() reports the number of consumed resource IDs.

remove(...) is the operation that actually deletes a resource from the heap. It also removes the resource from the nullifier set if it was already consumed. This makes remove(...) a cleanup operation rather than a normal consumption operation. Resource ids include the monotonic allocation counter, so a removed id is never reissued by the same heap. The heap nullifier set is independent of protocol-machine communication replay nullifiers, which remove(...) does not affect.

Merkle Commitments

The heap can derive Merkle commitments over its current state. MerkleTree::from_heap(...) builds a Merkle tree from active resources only. HeapCommitment::from_heap(...) combines the active-resource root, the nullifier root, and the allocation counter.

#![allow(unused)]
fn main() {
use telltale_runtime::heap::{HeapCommitment, MerkleTree};

let tree = MerkleTree::from_heap(&heap);
let proof = tree.prove(0);
let commitment = HeapCommitment::from_heap(&heap);
}

prove(...) generates an inclusion proof by leaf index in the current active-resource ordering. That ordering follows BTreeMap iteration over active ResourceId keys. resource_id_preimage(...), resource_leaf_preimage(...), nullifier_leaf_preimage(...), merkle_node_preimage(...), and heap_commitment_preimage(...) expose the tagged preimage rules directly. resource_leaf_hash(...), nullifier_leaf_hash(...), and merkle_node_hash(...) then hash those preimages. HeapCommitment::hash() then hashes a tagged preimage that contains the two roots and the counter.

Error Types

Heap operations use HeapError.

#![allow(unused)]
fn main() {
pub enum HeapError<H: Hasher = DefaultContentHasher> {
    NotFound(ResourceId<H>),
    AlreadyConsumed(ResourceId<H>),
    AlreadyExists(ResourceId<H>),
    TypeMismatch { expected: String, got: String },
    Other(String),
}
}

In the current implementation, NotFound and AlreadyConsumed are the main operational cases returned by heap methods. AlreadyExists and TypeMismatch are part of the public error vocabulary but are not heavily exercised by the basic heap operations shown above.

Encoding and Hashing Guarantees

The heap uses a versioned canonical binary format for resources. Every canonical heap value starts with the TTHP magic prefix, a little-endian encoding version, and a one-byte type tag. Strings and byte slices use explicit little-endian u32 length prefixes. Nested heap values are encoded as full canonical child values.

The heap also uses tagged preimages for ResourceId, resource-leaf hashes, nullifier-leaf hashes, Merkle internal nodes, and HeapCommitment::hash(). Those tagged preimages are part of the public heap contract.

Canonical Encoding Model

The heap uses a runtime-local canonical binary format. It does not reuse the type-level artifact encoding in Content Addressing. The current format is versioned by HEAP_ENCODING_VERSION.

Every canonical heap value starts with the same prelude. The prelude is the four-byte magic value TTHP, followed by the little-endian encoding version, followed by a one-byte type tag. The remainder of the byte sequence is the canonical body for that tagged value.

Strings and byte slices use explicit little-endian u32 length prefixes. Counters and numeric fields use little-endian integer encoding. Nested heap values are encoded as full canonical child values.

Covered Types

ChannelState encoding includes sender, receiver, queue length, and every queued MessagePayload in order. Message encoding includes source, destination, label, full payload bytes, and sequence number. Session and Value resources encode all of their current semantic fields.

The current canonical encoding also covers MessagePayload, ChannelState, Message, and Resource. Resource::Session includes role and type_hash. Resource::Value includes tag and full data.

Resource Identity

ResourceId remains allocation-unique in the current design. The heap computes it from a tagged preimage that contains the canonical resource bytes and the little-endian allocation counter. The digest uses the selected heap hasher, which defaults to DefaultHeapHasher.

This design keeps repeated allocations of identical semantic resources distinct. It also ties the ID contract directly to the canonical heap encoding boundary.

Merkle Leaves and Commitments

Active-resource leaves hash a tagged preimage that contains the ResourceId digest and the canonical resource bytes. Nullifier leaves hash a distinct tagged preimage that contains the ResourceId digest bytes. Internal Merkle nodes hash a tagged preimage that contains the left child digest and the right child digest. HeapCommitment stores the active-resource root, the nullifier root, and the allocation counter. HeapCommitment::hash() hashes a tagged preimage that contains those three values.

The allocation counter remains part of the authoritative commitment contract. This keeps the commitment aligned with allocation history rather than only current live content. The current heap model treats that history as semantically relevant.

Cross-Implementation Expectations

Another implementation should reproduce canonical bytes exactly. It should also preserve active-resource ordering and nullifier ordering exactly. The published heap test vectors are the conformance target for that behavior. The current published vector file is rust/runtime/tests/data/heap_vectors_v1.json.

The runtime heap now has a Lean parity mirror for canonical bytes and tagged preimages. That mirror lives in lean/Runtime/Resources/HeapModel.lean and is exercised through lean/Runtime/Tests/HeapParityRunner.lean.

The digest algorithm itself is still treated as an operational conformance target. Rust and Lean must agree on the published heap vectors exactly, but Lean does not currently reimplement BLAKE3. This section therefore defines both the boundary that an external embedder must match and the boundary that the current Rust↔Lean heap lane enforces.

See Rust-Lean Bridge and Parity for the Lean mirror boundary.

Determinism Contract

Heap operations are deterministic by contract. Active-resource ordering follows ResourceId ordering in the heap BTreeMap. Consumed-resource ordering follows the same ResourceId ordering in the nullifier BTreeSet. Merkle proof indices refer to that exact active-resource order.

Ordering Rules

Active resources are ordered by ResourceId. ResourceId ordering compares digest bytes first and allocation counter second. Heap::active_resources() uses that order because the heap stores resources in a BTreeMap.

Consumed resource IDs are ordered by the same ResourceId ordering. Heap::consumed_ids() uses that order because the heap stores nullifiers in a BTreeSet. Merkle leaves follow the iteration order of active_resources(). Merkle proof indices refer to that exact active-leaf order.

Replay Contract

Repeated executions of the same heap operation sequence must yield the same ResourceId values, proof paths, and HeapCommitment values. This contract is sequence-sensitive. If allocation order changes, the allocation counter changes. That changes ResourceId values and therefore changes commitments.

Commitment Authority

HeapCommitment is the authoritative cryptographic summary of heap state. HeapCommitment::hash() is the only supported single-value digest for that state. The heap no longer exposes a separate debug-only state fingerprint.

Order-Insensitive Operations

Some operations are deterministic even when their input lists are permuted. consume_many(...) is one example. If the same set of resource IDs is consumed successfully, the resulting nullifier set and heap commitment must match regardless of argument order.

Scope

This contract now has a focused Lean mirror. lean/Runtime/Resources/HeapModel.lean mirrors active/nullifier ordering, proof-index semantics, and deterministic replay for the published heap corpus. The published vectors and the strict Rust↔Lean heap parity suite are the current conformance boundary for other implementations.

Use Content Addressing for the type-level artifact identity system. Use Choreography Effect Handlers for choreography runtime integration. See Rust-Lean Bridge and Parity for the Rust↔Lean conformance lane.

Topology

Topology separates protocol logic from deployment configuration. Choreographies remain location free while topology definitions specify where roles execute.

Core Types

Locations capture how a role is deployed.

#![allow(unused)]
fn main() {
pub enum Location {
    Local,
    Remote(TopologyEndpoint),
    Colocated(RoleName),
}
}

The Local variant means in-process execution. The Remote variant stores a network endpoint. The Colocated variant ties a role to another role location.

Topology state holds role mappings, constraints, and an optional local-only shorthand.

#![allow(unused)]
fn main() {
pub struct Topology {
    pub mode: Option<TopologyMode>,
    pub locations: BTreeMap<RoleName, Location>,
    pub channel_capacities: BTreeMap<(RoleName, RoleName), ChannelCapacity>,
    pub constraints: Vec<TopologyConstraint>,
    pub role_constraints: BTreeMap<String, RoleFamilyConstraint>,
}
}

TopologyMode is intentionally narrow. The runtime only exposes a local shorthand. Deployment-specific discovery or orchestration belongs in transport or integration crates, not in telltale-runtime. TopologyConstraint and RoleFamilyConstraint add placement and role family rules.

#![allow(unused)]
fn main() {
pub enum TopologyMode {
    Local,
}

pub enum TopologyConstraint {
    Colocated(RoleName, RoleName),
    Separated(RoleName, RoleName),
    Pinned(RoleName, Location),
    Region(RoleName, Region),
}

pub struct RoleFamilyConstraint {
    pub min: u32,
    pub max: Option<u32>,
}
}

TopologyMode is parsed from the DSL value local. Older deployment-specific mode: values like per_role, kubernetes(...), or consul(...) now reject explicitly because runtime topology is transport agnostic. Role family constraints are used to validate wildcard and range roles.

RoleFamilyConstraint provides helper constructors and a validation method:

#![allow(unused)]
fn main() {
use telltale_runtime::RoleFamilyConstraint;

// Minimum-only constraint (unbounded max)
let unbounded = RoleFamilyConstraint::min_only(3);

// Bounded constraint with min and max
let bounded = RoleFamilyConstraint::bounded(2, 5);

// Validate a role count against the constraint
bounded.validate(4)?;  // Ok
bounded.validate(1)?;  // Err(BelowMinimum)
bounded.validate(6)?;  // Err(AboveMaximum)
}

Role-family constraints are explicit validation data. Parsed topologies and builder-created topologies preserve them, and generated inline topology helpers retain them, but the caller still supplies the resolved family count at runtime via validate_family.

DSL Syntax

Topologies are defined in .topology files or parsed from strings.

topology TwoPhaseCommit_Prod for TwoPhaseCommit {
    Coordinator: coordinator.prod.internal:9000
    ParticipantA: participant-a.prod.internal:9000
    ParticipantB: participant-b.prod.internal:9000

    role_constraints {
        Participant: min = 2, max = 5
    }

    channel_capacities {
        Coordinator -> ParticipantA: 4
        Coordinator -> ParticipantB: 4
    }

    constraints {
        separated: Coordinator, ParticipantA
        separated: Coordinator, ParticipantB
        region: Coordinator -> us_east_1
    }
}

The role_constraints block controls acceptable family sizes. The channel_capacities block sets per-edge capacity in bits (used for branching feasibility checks). The constraints block encodes separation, pinning, and region requirements. region: Role -> region_name is now executable topology data: validated roles resolve a concrete region, colocated roles inherit the peer region unless they declare the same region explicitly, and conflicting colocated region declarations reject deterministically.

Rust API

You can build topologies programmatically and validate them against choreography roles.

#![allow(unused)]
fn main() {
use telltale_runtime::{RoleFamilyConstraint, RoleName, Topology, TopologyEndpoint};

let topology = Topology::builder()
    .local_role(RoleName::from_static("Coordinator"))
    .remote_role(
        RoleName::from_static("ParticipantA"),
        TopologyEndpoint::new("localhost:9001").unwrap(),
    )
    .remote_role(
        RoleName::from_static("ParticipantB"),
        TopologyEndpoint::new("localhost:9002").unwrap(),
    )
    .role_family_constraint("Participant", RoleFamilyConstraint::bounded(2, 5))
    .build();

let roles = [
    RoleName::from_static("Coordinator"),
    RoleName::from_static("ParticipantA"),
    RoleName::from_static("ParticipantB"),
];
let validation = topology.validate(&roles);
assert!(validation.is_valid());
}

Topology::builder produces a TopologyBuilder with fluent helpers. Topology::validate checks that all roles referenced in the topology and constraints exist in the choreography. Topology::region_for_role(&role) resolves the effective region after colocated inheritance and reports conflicting region declarations as validation errors.

For reconfiguration and recovery flows, the same topology can export canonical placement and transport-boundary artifacts for an active member set:

#![allow(unused)]
fn main() {
let placements = topology.placement_observations_for_roles(["Coordinator", "ParticipantA"])?;
let boundaries = topology.transport_boundaries_for_roles(["Coordinator", "ParticipantA"])?;
}

placement_observations_for_roles(...) returns transport-agnostic facts per member: local/remote/colocated kind, optional endpoint, and resolved region. transport_boundaries_for_roles(...) derives canonical in-process, shared-memory, and network boundaries plus cross-region markers. These are the runtime-facing inputs used by deterministic multi-step reconfiguration plans and their recovery artifacts.

Explicit family-cardinality checks use the same topology object:

#![allow(unused)]
fn main() {
topology.validate_family("Participant", 3)?;
assert!(topology.validate_family("Participant", 1).is_err());
}

Topologies can also be loaded from DSL files.

#![allow(unused)]
fn main() {
let parsed = Topology::load("deploy/prod.topology")?;
let topology = parsed.topology;
}

Topology::load returns a ParsedTopology that includes both the topology and the target protocol name.

TopologyHandler

TopologyHandler wraps transport selection and routing for a role.

#![allow(unused)]
fn main() {
use telltale_runtime::{RoleName, TopologyHandler};

let handler = TopologyHandler::local(RoleName::from_static("Alice"));
handler.initialize().await?;
}

The local constructor sets TopologyMode::Local and creates in-process transports. For custom layouts, use TopologyHandler::new or the builder with a Topology. If you need Kubernetes, Consul, or another discovery system, implement that in a transport or integration crate and feed the runtime explicit Location::Remote endpoints or another transport-facing adapter boundary.

Generated protocols include helpers under Protocol::topology, including Protocol::topology::handler(role) and Protocol::topology::with_topology(topology, role). These return a TopologyHandler for the selected role.

Generated Topology Helper Surface

tell! emits a topology helper module per protocol. The generated surface follows this shape:

#![allow(unused)]
fn main() {
pub mod topology {
    pub fn handler(role: Role) -> TopologyHandler;
    pub fn with_topology(topology: Topology, role: Role) -> Result<TopologyHandler, String>;

    pub mod topologies {
        pub fn dev() -> Topology;
        pub fn dev_handler(role: Role) -> Result<TopologyHandler, String>;
        pub fn prod() -> Topology;
        pub fn prod_handler(role: Role) -> Result<TopologyHandler, String>;
    }
}
}

handler(role) builds a local topology handler. with_topology(topology, role) validates role coverage, placement constraints, and branch-capacity constraints and returns a role-bound handler. Inline named topologies preserve declared role-family constraints as topology data, but callers still invoke validate_family(...) explicitly because family counts are runtime inputs.

Usage pattern:

#![allow(unused)]
fn main() {
let local = MyProtocol::topology::handler(Role::Alice);
let prod = MyProtocol::topology::topologies::prod_handler(Role::Alice)?;
let custom = MyProtocol::topology::with_topology(custom_topology, Role::Alice)?;
}

This pattern shows local default setup, named topology setup, and custom topology setup in one place.

Transport Selection

Transport selection is based on role locations.

#![allow(unused)]
fn main() {
use telltale_runtime::topology::{TransportFactory, TransportType};

let transport_type = TransportFactory::transport_for_location(
    &RoleName::from_static("Alice"),
    &RoleName::from_static("Bob"),
    &topology,
)?;
assert!(matches!(transport_type, TransportType::Tcp));
}

TransportFactory::create realizes loopback Location::Remote endpoints through a deterministic TCP transport on native targets. TopologyHandler uses the same remote slice for with_topology(...) helpers, so generated topology public-path tests exercise real loopback message delivery instead of an intent-only placeholder. The runtime does not encode discovery products or managed deployment backends directly. Those belong outside the runtime API.

Those transport decisions are also visible through transport_boundaries_for_roles(...), which means topology-aware reconfiguration tests compare the same canonical boundary summary across direct execution, snapshot/restore, and bridge-mediated recovery runs.

Lean Correspondence

The Lean formalization for topology is in lean/Protocol/Spatial.lean. Projection correctness does not depend on topology data, so location checks are enforced during deployment instead of compilation.

See Choreographic DSL for role declarations and Choreography Effect Handlers for choreography handler usage patterns.

Generated public-path validation

Generated topology helpers use fully qualified public runtime types and import only their containing protocol’s Role (plus the parent helper for named presets). Optional placement, region, capacity and role-family definitions do not produce unused imports in protocols that omit them. The generated topology public-path smoke compiles and executes both its temporary library and test under #![deny(warnings)], including basic, branching and named presets. It retains the caller’s Cargo flags so validation shares existing artifact settings.

Capability Model

This page is the canonical reference for Telltale’s first-class protocol-critical capability model.

The model is intentionally narrow. It covers protocol-critical authority, evidence, finalization, handoff, and reconfiguration/runtime-upgrade semantics. It does not define a general-purpose application authorization framework.

Scope Boundary

In scope:

  • protocol-critical capability semantics that change protocol-visible truth
  • replay-visible evidence and finalization objects
  • ownership and receipt/handoff/reconfiguration transition artifacts
  • theorem-pack and runtime admission gates

Out of scope:

General host application authorization is out of scope.

  • arbitrary user/resource policy languages
  • product-specific auth delegation systems unrelated to protocol semantics
  • host-only policy decisions that cannot be modeled as typed Rust/Lean objects

Four Capability Classes

ClassMain questionRepresentative objects
admissionmay this runtime/profile/configuration execute at all?theorem-pack eligibility, runtime contracts, determinism-profile gates
ownershipwho may currently mutate one live session or fragment?OwnershipCapability
evidencewhy is a protocol-critical branch or canonicalization step justified?ReadinessWitness, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, PublicationEvent
transitionwhat explicit object authorizes transfer, handoff, cutover, or reconfiguration?OwnershipReceipt, SemanticHandoff, DelegationReceipt, RuntimeUpgradeArtifact, reconfiguration transition artifacts

The canonical Rust boundary is rust/machine/src/capabilities.rs. The canonical Lean boundary is lean/Runtime/Proofs/CapabilityModel.lean together with the theorem-pack and conservation proof surfaces.

Canonical Lifecycle States

StateMeaning
issuedobject exists and may be consumed or transitioned later
consumedobject was used exactly once on its sanctioned path
rejectedobject was denied as invalid, stale, or insufficient
invalidatedobject became unusable because later semantic state revoked it
committedtransition object became canonical
rolled_backtransition object failed and did not become canonical
expiredobject aged out of its validity window

Not every class uses every lifecycle state. For example, ownership uses issued, invalidated, expired, and rejected, while transition artifacts commonly use issued, committed, rolled_back, rejected, and invalidated.

Finalization State Machine

Finalization is the explicit bridge from protocol observation to canonical truth.

StageMeaning
observedonly observational input exists
authoritativeauthoritative evidence exists, but proof-bearing success is incomplete
materializedproof-bearing success exists, but canonical publication/handle pairing is incomplete
canonicalpublication/materialization/handle conditions are satisfied and the result may be consumed as protocol truth
invalidateda handoff or transition revoked the path before canonical reuse
rejectedproof-bearing publication or repair failed

The key runtime/Lean objects are:

  • ProtocolMachineFinalization
  • FinalizationPath
  • FinalizationReadClass
  • FinalizationStage

Observed input never becomes canonical directly. It must pass through the explicit authoritative/materialization/publication path.

Language Mapping

DSL formCapability classResulting semantic object family
authoritative let x = check ...evidenceAuthoritativeRead, readiness/evidence-bearing path
observe let x = observe ...evidenceObservedRead
let receipt = transfer ...transitionOwnershipReceipt / transfer receipt path
publish witness as PublicationevidencePublicationEvent
materialize proof from PublicationevidenceMaterializationProof, CanonicalHandle
handoff op to Role with receipttransitionSemanticHandoff

Runtime upgrade and reconfiguration are first-class transition semantics in the runtime and Lean model, but they are not choreography DSL statements today.

Runtime Meaning

The runtime uses one canonical lifecycle audit and one canonical semantic-object bundle:

  • ProtocolMachine::capability_lifecycle_audit_log()
  • ThreadedGuestRuntime::capability_lifecycle_audit_log()
  • ProtocolMachine::semantic_objects()
  • ProtocolMachine::protocol_machine_finalization()

Timeout expiry is modeled strictly: the runtime stores and later expires the exact issued TimeoutWitness, rather than reconstructing expiry from a bare clock map later.

Transfer/handoff and runtime upgrade/reconfiguration are also explicit. They emit typed transition artifacts rather than relying on hidden host-local state.

ProtocolMachineConfig.host_contract_assertions is a typed HostContractMode. The default Enforced mode checks handler identity stability, deterministic topology-event ordering, and transfer-event audit coverage before those host-supplied facts are accepted into the machine. The RelaxedTestOnly mode is reserved for targeted tests that intentionally violate those contracts. It is rejected with DeterminismMode::Full.

Lean and Bridge Surfaces

The Lean model exports this surface through:

  • lean/Runtime/Proofs/CapabilityModel.lean
  • lean/Runtime/Tests/ProtocolMachineRunner.lean

The strict Rust↔Lean correspondence lane checks the capability/finalization surface through:

  • rust/bridge/tests/capability_model_correspondence.rs
  • toolkit/xtask/src/checks/lean_bridge_strict.rs

That lane keeps canonical vs invalidated finalization paths and committed-cutover vs rolled-back runtime-upgrade artifacts executable and claim-visible.

Scope Decision Criteria

A concern is eligible to move into the protocol-machine/effect/DSL boundary when all of the following hold:

  1. It changes protocol-critical behavior rather than only presentation or app-internal behavior.
  2. It can be represented as typed outcomes, evidence, receipts, or obligations rather than opaque host internals.
  3. It is observable at the replay/audit/effect-trace boundary.
  4. The protocol machine can enforce or validate it at a meaningful boundary.
  5. Lean/Rust correspondence can model it without inventing a separate host-language semantics.

A concern should stay host-only when any of the following hold:

  1. It is mainly UI/view/reduction logic.
  2. It depends on implementation-specific nondeterministic runtime internals rather than typed protocol observations.
  3. It cannot be expressed as typed evidence/outcomes without dragging in a general-purpose application semantics.
  4. It is not part of the safety-visible or replay-visible protocol interface.

Classification

BoundaryExamples
Move into protocol machine/effect layertyped effect outcomes, authority/evidence/receipt objects, explicit timeout/cancellation/failure semantics, structured authority traces
Move into DSLcase/of pattern matching, unions and type alias, let-bound evidence values, check syntax, effect/uses declarations
Keep host-onlyUI/view/reduction architecture, frontend state taxonomies, storage/retry/transport internals, rendering-oriented observed state

Host Runtime Disposal Boundary

Targeted close and reap is an operation of the exclusively owned Rust runtime. Its acknowledgment proves retirement of execution custody after any owned worker scope completes. The compact summary grants no admission, ownership, evidence, or transition capability and cannot authorize a protocol outcome. Application owners retain their protocol terminal publication and authenticated evidence obligations. Session Lifecycle defines the runtime retirement contract. Rust-Lean Parity records the host-lifecycle scope.

Typed Invocation Acceptance

A host response is an observation until the machine accepts it against the original request. Register state may change only for a successful outcome with the exact response family required by that request. Failure and blocked outcomes cannot authorize a state update by attaching a success-shaped payload. A malformed successful outcome is a failed contract, not a successful publication or a successful child in effect composition. The raw exchange remains available as an observation.

Nominal authority invocation additionally requires a declared operation, role-scoped execution under its actual session owner and generation, typed arguments and outcome, and one live continuation. Unknown declarations, stale owners, duplicate completion and invalidated continuations must fail before publication or continuation advancement. The generic runtime.step boundary does not yet implement this nominal contract. Naming an action is not dispatch or proof that the contract was checked.

Capability Admission

This document defines the capability gates that control runtime admission and profile selection.

Admission is not the same thing as current ownership. Admission answers whether a runtime/profile/configuration is allowed in principle. Ownership answers who may currently drive session-local host mutation for one live session or fragment.

Within the broader first-class protocol-critical capability model, admission is only one class beside ownership, evidence, and transition capability surfaces. Admission decides whether execution is allowed in principle. It does not itself prove current session authority or canonical semantic truth. It also does not provide a general-purpose application authorization system.

Gate Layers

Admission is enforced across Lean theorem-pack surfaces and Rust runtime checks.

LayerMain API
Lean theorem-pack facadeRuntime/Proofs/TheoremPack/API.lean
Lean capability inventoryRuntime/Proofs/TheoremPack/Inventory.lean
Lean admission logicRuntime/Adequacy/EnvelopeCore/AdmissionLogic.lean
Rust runtime gatesrust/machine/src/runtime_contracts.rs
Rust composition admissionrust/machine/src/composition.rs

The canonical cross-cutting capability taxonomy now lives in:

  • rust/machine/src/capabilities.rs
  • lean/Runtime/Proofs/Capabilities.lean

Runtime Gate Flow

Rust runtime admission uses a fixed gate sequence.

StepFunctionResult class
advanced mode checkrequires_protocol_machine_runtime_contractsboolean requirement
runtime admissionadmit_protocol_machine_runtimeAdmitted or RejectedMissingContracts
profile requestrequest_determinism_profileselected profile or None
unified gateenforce_protocol_machine_runtime_gatesAdmitted, RejectedMissingContracts, or RejectedUnsupportedDeterminismProfile

The unified gate is the admission decision used by higher-level runtime loaders.

Admission vs Ownership

These concepts are intentionally separate.

QuestionAdmission answersOwnership answers
can this runtime mode/profile be used?yesno
does this theorem-pack/runtime-contract inventory admit the feature?yesno
may this caller mutate session-local host state right now?noyes
does this caller hold current fragment/session authority?noyes

Practical consequence:

  • passing enforce_protocol_machine_runtime_gates(...) does not authorize session-local mutation
  • hosts still need a live ownership capability such as OwnedSession
  • stale-owner rejection can occur even when admission remains valid
  • a choreography uses Runtime, Audit declaration states external protocol dependencies. It does not replace runtime admission or ownership checks

Effect Interfaces vs Admission

Language-level effect interfaces and runtime admission solve different problems.

SurfaceMain question
effect declarationwhat typed host operation does the protocol depend on?
uses clausewhich named external interfaces may this protocol call?
admission gatemay this runtime/profile/configuration run at all?
ownership capabilitywho may currently drive one live session or fragment?

Nominal effect interfaces therefore narrow protocol dependencies without weakening theorem-pack admission or ownership enforcement.

Determinism Profiles

Determinism profiles are validated against artifacts and capability switches.

ProfileRust enumArtifact flag
fullDeterminismMode::Fulldeterminism_artifacts.full
modulo effect traceDeterminismMode::ModuloEffectsdeterminism_artifacts.modulo_effect_trace
modulo commutativityDeterminismMode::ModuloCommutativitydeterminism_artifacts.modulo_commutativity
replayDeterminismMode::Replaydeterminism_artifacts.replay

Non-full profiles also require can_use_mixed_determinism_profiles.

Theorem-Pack Capability Inventory

The theorem-pack inventory publishes machine-readable capability keys. Keys are strings defined in lean/Runtime/Proofs/TheoremPack/Inventory.lean, not Lean theorem names. A key is present when the corresponding profile artifact is attached. For example, quorum_geometry_safety is backed by Distributed.QuorumGeometry.no_conflicting_commits_of_protocol, fork_exclusion_of_protocol, and safe_finality_of_protocol.

Inventory familyExample keys
protocol safety and livenesstermination, output_condition_soundness
distributed impossibility and safetyflp_impossibility, cap_impossibility, quorum_geometry_safety
distributed deployment familiespartial_synchrony_liveness, reconfiguration_safety, atomic_broadcast_ordering
advanced envelope familiesconsensus_envelope, failure_envelope, protocol_machine_envelope_adherence, protocol_machine_envelope_admission, protocol_envelope_bridge
classical transport familiesclassical_foster, classical_maxweight, classical_ldp, classical_functional_clt

Rust capability snapshots should align with this inventory for release and admission claims.

Transported Theorem Boundary

Not every transported theorem family influences shipped runtime admission. Telltale now tracks that boundary explicitly in Lean and Rust.

Usage classMeaningCurrent examples
runtime_critical_instantiated_premisematerially influences a shipped runtime gate/guarantee surfaceprotocol_machine_envelope_adherence, protocol_machine_envelope_admission, protocol_envelope_bridge
black_box_premise_onlyused by verifier/reporting surfaces, but not consumed directly by shipped Rust runtime admissionflp_impossibility, quorum_geometry_safety, failure_envelope
documentation_background_onlycarried for theorem-program inventory/docs onlyclassical_foster, classical_functional_clt

The canonical ledger lives in:

  • lean/Runtime/Proofs/TheoremPack/AdmissionBoundary.lean
  • rust/machine/src/runtime_contracts.rs

One runtime-critical theorem family is intentionally marked as a boundary rather than a shipped Rust admission dependency today:

  • byzantine_safety_characterization is consumed by a Lean theorem-pack gate alias, but Rust runtime admission does not currently use it. That gap is now explicit rather than implicit.

Source-Derived Rows

The following rows are source-derived and checked against telltale_machine::transported_theorem_boundary() by rust/bridge/tests/docs_contract_tests.rs.

KeyUsage classRust runtime admissionLean runtime gateAssumption boundary
byzantine_safety_characterizationruntime_critical_instantiated_premisenoyesLean theorem-pack gate only. Rust runtime admission does not currently consume this key.
protocol_machine_envelope_adherenceruntime_critical_instantiated_premiseyesyesnone
protocol_machine_envelope_admissionruntime_critical_instantiated_premiseyesyesnone
protocol_envelope_bridgeruntime_critical_instantiated_premiseyesyesnone

Rust admission of these three envelope keys requires Lean-produced evidence. execution_profile_supported consults the profile’s LeanEvidenceInventory (loaded from the Lean theoremInventory export) rather than self-asserted eligibility flags. Default profiles carry no evidence and are not admitted. assume_envelope_evidence_for_testing() is the explicit test-only opt-in. See docs/804_theorem_pack_inputs.md.

Composition Admission

Composed runtime admission in rust/machine/src/composition.rs enforces both proof artifacts and runtime gates.

RequirementFailure mode
link_ok_full compatibility evidenceMissingCompatibilityProof
runtime contracts for advanced modeMissingRuntimeContracts
required scheduler capabilityMissingCapability
required determinism capabilityMissingCapability
output-condition gating capabilityMissingCapability
memory budgetBudgetExceeded

This path guarantees that admitted bundles carry both semantic and operational evidence.

When composition metadata defines fragment or bundle boundaries, runtime ownership should derive from that metadata rather than from ad hoc host-side reconstruction. Admission decides whether the bundle may run. Ownership decides who may currently drive it.

Runtime Upgrade Admission

Typesafe runtime upgrade is treated as a specialized transition family layered on top of composition admission, not as an unmodeled host escape hatch.

The runtime-upgrade admission contract is carried by:

  • RuntimeUpgradeRequest
  • RuntimeUpgradeCompatibility
  • RuntimeUpgradeArtifact

An admitted upgrade must make the following transition requirements explicit:

RequirementCurrent Rust enforcement
execution-profile compatibilityRuntimeUpgradeExecutionConstraint validated in rust/machine/src/composition.rs
ownership continuity across the first cutoveroverlap check against the currently active member set
pending-effect treatmentPendingEffectTreatment must be explicit and fail closed on invalidated/blocked obligations
canonical publication continuityCanonicalPublicationContinuity rejects invalidation when continuity is required

The runtime records staged, admitted, committed-cutover, rolled-back, and failed artifacts explicitly. Those artifacts are replay-visible through ReconfigurationRuntimeSnapshot.runtime_upgrades.

Admission Diagnostics

Lean and Rust both expose structured rejection categories.

SurfaceRejection classes
Lean admission logicmaxDiffExceeded, eqSafeNotSupported, missingRequiredProfiles
Rust runtime gateRejectedMissingContracts, RejectedUnsupportedDeterminismProfile
Rust compositiontyped CompositionError variants with capability key strings

These categories are intended for machine-visible reporting and CI gate failures.

Governance and CI

Admission and capability drift are controlled by repository lanes.

CheckCommand
runtime capability gate shapejust check-capability-gates
theorem-pack release conformancejust check-release-conformance
Protocol-machine parity suitejust check-parity --suite
parity type and schema policyjust check-parity --types
consolidated parity policyjust check-parity --all

Authority Language Surface

This page records the current authority/failure-oriented DSL surface added on top of the choreography parser.

It is the current reference page for language-level authority checks, typed failure branching, and nominal effect interfaces. It is intentionally narrower than a full generalized effect language. General host application authorization is out of scope.

Current Surface

Top-level declarations:

  • type Name = | Ctor | Ctor(Payload)
  • type alias Name =
  • {
  • field : Type
  • }
  • effect Runtime
  • authoritative ready : Session -> Result CommitError ReadyWitness
  • protocol Flow uses Runtime =
  • roles Coordinator, Worker
  • authoritative let witness = check Runtime.ready(session)

Protocol-local forms:

  • authoritative let binding = check Runtime.op(args)
  • observe let binding = observe Effect.op(args)
  • let receipt = transfer Session from A to B
  • let binding = ... in ...
  • case expr of | Ok(value) => ... | Err(reason) => ...
  • timeout 5s Coordinator at ... on timeout => ... on cancel => ...
  • choice Coordinator at | Commit when check Runtime.ready(session) yields witness => ...

Review Sketches

These are the short canonical examples for the current reviewed language additions.

Nominal Effects and uses

effect Runtime
  authoritative ready : Session -> Result CommitError ReadyWitness
  {
    class : authoritative
    progress : may_block
    region : fragment
    agreement_use : required
    reentrancy : reject_same_fragment
  }
  observe watchPresence : Session -> PresenceView
  {
    class : observational
    progress : immediate
    region : session
    agreement_use : forbidden
    reentrancy : allow
  }

protocol Flow uses Runtime =
  roles Coordinator, Worker
  authoritative let witness = check Runtime.ready(session)
  Coordinator -> Worker : Commit

This is the smallest effect declaration plus dependency declaration that the language accepts.

Result with case/of

authoritative let readiness = check Runtime.ready(session) in
  case readiness of
    | Ok(witness) =>
        Coordinator -> Worker : Commit(witness)
    | Err(reason) =>
        Coordinator -> Worker : Cancel(reason)

This is the canonical typed branching form for authority checks.

Maybe

case maybeReceipt of
  | Just(receipt) =>
      Coordinator -> Worker : UseReceipt(receipt)
  | Nothing =>
      Coordinator -> Worker : MissingReceipt

This keeps absence explicit instead of falling back to implicit defaults.

Custom Union and Alias

type CommitError =
  | TimedOut
  | Cancelled

type alias ReadyWitness =
  { epoch : Int
  }

Custom unions and aliases name failure and evidence values directly in the DSL.

Evidence Binding with let

let receipt = transfer Session from Coordinator to Worker
handoff acceptInvite to Worker with receipt

Ordinary let syntax is the only binding form used for receipts and other authority artifacts.

Timeout and Cancellation

timeout 5s Coordinator at
  Worker -> Coordinator : Ready
on timeout =>
  Coordinator -> Worker : Cancel
on cancel =>
  Coordinator -> Worker : Cancelled

Timeout and cancellation are explicit protocol branches, not host-only control flow.

Evidence Guard

choice Coordinator at
  | Commit when check Runtime.ready(session) yields witness =>
      Coordinator -> Worker : Commit(witness)
  | Abort =>
      Coordinator -> Worker : Abort

Evidence guards bind the witness directly at the branch point that authorizes the action.

Linear Single-Use Binding

let receipt = transfer Session from Coordinator to Worker
handoff acceptInvite to Worker with receipt

Transfers produce first-class transition receipts. The compiler requires each receipt to be consumed exactly once, and the runtime/Lean model treats the receipt as a transition artifact rather than an untyped helper value.

Local Helper Expression

authoritative let readiness = check Runtime.ready(session) in
  case readiness of
    | Ok(witness) =>
        Coordinator -> Worker : Commit(witness)
    | Err(reason) =>
        Coordinator -> Worker : Cancel(reason)

let ... in ... keeps small authority decisions local instead of pushing them into host callbacks.

No Implicit Default

case readiness of
  | Ok(witness) =>
      Coordinator -> Worker : Commit(witness)
  | Err(reason) =>
      Coordinator -> Worker : Cancel(reason)

Protocol-critical matches must be exhaustive. Implicit catch-all masking is rejected.

Typed External Query

authoritative let readiness = check Runtime.ready(session)

Typed external queries always enter the protocol through explicit check expressions.

Result and Maybe

The DSL currently treats Result and Maybe as built-in naming conventions rather than a separate kind system.

  • Result constructors: Ok, Err
  • Maybe constructors: Just, Nothing

Current compiler rules:

  • case/of over Result must include both Ok and Err
  • case/of over Maybe must include both Just and Nothing
  • implicit catch-all masking is not supported

Let and Linearity

let is the normal binding form for transition receipts and non-authoritative query results. authoritative let and observe let are the explicit binding forms for protocol-critical authoritative and observational reads.

DSL Capability Mapping

DSL formCapability classLifecycle emphasis
authoritative let x = check ...evidenceissued -> consumed/rejected
observe let x = observe ...evidenceobserved-only input, never canonical directly
let receipt = transfer ...transitionissued -> committed/rolled_back/rejected/invalidated
publish witness as Publicationevidenceauthoritative -> published
materialize proof from Publicationevidencematerialized/rejected
handoff op to Role with receipttransitioncommitted/rolled_back/invalidated
case/of over evidencecontrol-flow over evidencemust preserve linear assets and remain exhaustive
timeout ... on timeout ... on cancel ...evidence/transition-adjacent control-flowexplicit timeout/cancellation outcomes with replay-visible lifecycle

Current linear classification:

  • transfer ... bindings are linear first-class transition receipts
  • authoritative let check ... bindings are first-class evidence reads
  • observe let observe ... bindings are first-class observational reads and may not be used where authoritative evidence is required

Current linear rules:

  • linear bindings must be consumed exactly once
  • duplicate use is rejected
  • implicit discard is rejected

Current cross-construct rule:

  • the implementation counts use across subsequent statements and across explicit branch bodies
  • the parser and protocol-machine surface support choice, case, timeout, loop, par, call, and recursion with explicit linear preservation checks
  • rejected programs fail closed when linear assets diverge across branches, timeout/cancel paths, loop iterations, recursive unfoldings, or parallel arms
  • observational bindings remain separated from authoritative evidence under these control-flow forms

Explicit Scope Boundary

The DSL currently gives first-class meaning to:

  • authoritative reads
  • observed reads
  • transfer receipts
  • publication events
  • materialization proofs
  • canonical handles
  • semantic handoffs

The DSL does not currently give first-class syntax to runtime upgrades or reconfiguration statements. Those remain runtime/model surfaces, not choreography statements, until they have a real shared Rust and Lean meaning.

Authority Metatheory Boundary

The authority theorem story is intentionally narrower than the executable DSL surface.

Current proof-plane split:

  • ordinary communication, recursion, and other coordination-only structure remain part of the session-typed theorem story
  • the supported authority-bearing theorem slice lives in the protocol-machine semantic-object layer, not in a generalized session-type extension

Current supported authority theorem slice:

  • evidence-bearing reads introduced by authoritative let, observe let, and evidence guards
  • canonical publication/materialization paths introduced by publish ... as ... and materialize ... from ...
  • explicit progress contracts for parity-critical operations protecting those paths

Current runtime-semantic-only authority surfaces:

  • case/of
  • timeout ... on timeout ... on cancel ...
  • par
  • transfer-produced receipts and their consumption
  • handoff
  • dependent work
  • explicit commitment lifecycle forms such as begin, await, resolve, and invalidate

These forms name protocol-machine semantic obligations. Concrete executable lowering is entry-point-specific, as described in the source-derived support matrix and prepared enclosing-operation slice. Parser acceptance, generated metadata and communication projection do not establish that an authority obligation is executed. Protocol-machine conservation theorems retain their declared premises and do not supply a missing source-to-machine lowering. These forms remain outside the smallest authority-specific theorem slice.

Effect Declarations and Uses

effect declarations are nominal interfaces.

Current validation rules:

  • effect interface names must be unique
  • operation names must be unique within an effect
  • effect operations may be classified as authoritative, command, or observe
  • protocol ... uses ... may reference only declared effects
  • check Effect.op(...) and evidence guards may reference only:
    • effects named in uses
    • operations declared on those effects
  • check Effect.op(...) may not target observe operations
  • observe Effect.op(...) may reference only observational operations

This is the current clean-break contract. There is no fallback to implicit host knowledge. Lowering is expected to preserve this split into the runtime semantic-object family as AuthoritativeRead versus ObservedRead. Observational reads are not allowed to become proof-bearing success or canonical publication by repair.

Typed Host Effect Invocation

check Effect.op(...) is the language-level way to invoke typed host effects.

effect Runtime
  authoritative ready : Session -> Result CommitError ReadyWitness
  {
    class : authoritative
    progress : may_block
    region : fragment
    agreement_use : required
    reentrancy : reject_same_fragment
  }

protocol Flow uses Runtime =
  roles Coordinator, Worker
  authoritative let readiness = check Runtime.ready(session)
  Coordinator -> Worker : Commit

Current contract:

  • the protocol must declare the effect in uses
  • the operation must exist on the named nominal interface
  • check is for authoritative or command operations
  • observe is for observational operations
  • lowering stays centered on the existing protocol-machine invoke boundary
  • effect observations keep the nominal interface and operation identity for replay/audit surfaces

Projection and Theory Boundary

Current projection behavior is intentionally explicit:

  • let is treated as local-only and projects through to its continuation
  • case/of is currently rejected by projection
  • timeout ... on timeout ... on cancel ... is currently rejected by projection
  • accepted authority-aware choice, call, loop, par, and recursion forms remain protocol-machine executable, and the subset without other blockers remains session-projectable

This preserves MPST clarity while the explicit projection rules are still being designed.

Theory conversion is narrower than projection today:

  • let, case/of, and timeout must be lowered further before conversion to theory-facing global types
  • choice, call, counted loops, and recursion convert today when the surrounding protocol stays in the common subset
  • par is session-projectable and protocol-machine executable, but it still has no theory-facing global-type conversion and fails closed with an explicit Parallel blocker

Source-Derived Support Matrix

The following rows are source-derived from the checked authority fixture corpus and validated by rust/bridge/tests/docs_contract_tests.rs.

Fixture surfaceProtocol-machine executableSession-projectableTheory-convertibleAuthority theorem tierExplicit blocker
call with plain communicationyesyesyessession_typed_coordinationnone
choice with observational bindingyesyesnoevidence_publication_semantic_objectsAuthorityBinding
counted loop with authoritative bindingyesyesnoevidence_publication_semantic_objectsAuthorityBinding
recursion with authoritative bindingyesyesnoevidence_publication_semantic_objectsAuthorityBinding
par with observational bindingyesyesnoruntime_semantic_onlyAuthorityBinding
case/of with authoritative bindingyesyesnoruntime_semantic_onlyAuthorityBinding
timeout with observational bindingyesyesnoruntime_semantic_onlyAuthorityBinding

Explanatory prose below remains hand-written. The table above is the checked contract surface for the reviewed authority/control-flow subset.

Lowering Boundary

The current lowering split is:

  • parser/AST surface in rust/language
  • protocol-machine effect execution boundary in rust/machine
  • typed handler boundary centered on protocol-machine invoke

Current intent:

  • effect declarations become the source for generated Rust host interfaces
  • check Effect.op(...) remains a DSL-level reference to a typed external query
  • execution must still cross the same typed handler-obligation boundary rather than inventing a second host channel
  • the Lean bridge statement for nominal effect declarations stays centered on the existing protocol-machine invoke obligation rather than a separate effect-language proof surface

Observable Trace Model

Language-level effect usage is expected to remain observable through the same runtime/effect tracing surfaces used by protocol-machine replay and effect-bisimulation work.

Current design rule:

  • language-level authority checks and evidence-sensitive decisions must map to explicit effect operations and explicit protocol-machine-visible events
  • missing or ambiguous authoritative input must become typed failure, never silent fallback success
  • canonical replay artifacts must preserve these decisions as structured semantic audit records rather than opaque host-only log text

Source-Derived Boundary Rows

The rows below are source-derived and checked against telltale_machine::protocol_critical_capability_boundary() by rust/bridge/tests/docs_contract_tests.rs.

SurfaceClassLifecycleRust boundaryLean boundaryRationale
runtime_admissionadmissionissued, rejectedrust/machine/src/runtime_contracts.rslean/Runtime/Proofs/TheoremPack/AdmissionBoundary.leanAdmits or rejects runtime/profile execution before protocol-critical execution begins.
theorem_pack_capabilitiesadmissionissued, rejectedrust/machine/src/composition.rslean/Runtime/Proofs/TheoremPack/API.leanCarries proof-backed eligibility that higher-level runtime admission consumes.
ownership_capabilityownershipissued, invalidated, expired, rejectedrust/machine/src/session/overview.rslean/Runtime/Proofs/Conservation/Authority.leanProves which actor may currently mutate session-local protocol-critical state.
readiness_witnessevidenceissued, consumed, rejected, invalidated, expiredrust/machine/src/session/overview.rslean/Runtime/Proofs/AuthorityMetatheory.leanJustifies a protocol-critical readiness decision under one live owner generation.
authoritative_readevidenceissued, consumed, rejectedrust/machine/src/semantic_objects.rslean/Runtime/Proofs/Conservation/Evidence.leanCarries evidence-bearing protocol input that may author canonical truth.
materialization_proofevidenceissued, consumed, rejectedrust/machine/src/semantic_objects.rslean/Runtime/Proofs/Conservation/Evidence.leanWitnesses proof-bearing success on the sanctioned materialization path.
canonical_handleevidenceissued, consumed, rejected, invalidatedrust/machine/src/semantic_objects.rslean/Runtime/Proofs/Conservation/Evidence.leanProvides the strong reference required on parity-critical follow-on paths.
ownership_receipttransitionissued, committed, rolled_back, rejected, invalidated, expiredrust/machine/src/session/overview.rslean/Runtime/Proofs/Conservation/Authority.leanStages and commits explicit ownership transfer rather than ambient authority mutation.
semantic_handofftransitioncommitted, rolled_back, rejected, invalidatedrust/machine/src/semantic_objects.rslean/Runtime/Proofs/Conservation/Authority.leanRepresents explicit protocol-visible authority transfer and old-owner revocation.
reconfiguration_transitiontransitionissued, committed, rolled_back, rejectedrust/machine/src/composition.rslean/Runtime/Proofs/ReconfigurationObserver.leanCaptures protocol-critical cutover and membership/runtime transition artifacts.

Runtime Meaning

Language-level authority checks lower into the existing runtime authority surfaces:

  • check Effect.op(...) lowers to the typed protocol-machine effect boundary
  • successful protocol-critical checks produce explicit evidence/witness objects
  • invalid or missing evidence fails closed
  • timeout and cancellation become explicit observable/runtime-audit outcomes
  • transfer/delegation emits explicit receipts and audit records
  • stale capability, receipt, and witness reuse are rejected and retained in the lifecycle audit

This keeps the language aligned with EffectHandler typed outcomes, OwnedSession ownership capability checks, the witness family (ReadinessWitness, CancellationWitness, TimeoutWitness), and semantic_audit_log replay-visible event ordering.

Fail-Closed Rules

Protocol-critical authority must not degrade into ambient host heuristics.

  • no implicit wildcard/default masking for Result and Maybe
  • no ad hoc “missing data means false” authority checks
  • no silent reuse of stale capabilities or receipts
  • no hidden host-only branch decisions outside typed effect queries

This design does not create a second host/runtime bridge. The authoritative path is: declare a nominal effect, name it in uses, invoke it with check Effect.op(...), lower it into the existing typed protocol-machine invoke boundary, and preserve its outcome in effect/audit/replay surfaces.

Communication Artifact Refusal

Communication projection remains a type-level abstraction. Plain, non-linear identifier and constructor bindings have no invocation node and may project through to communication. Their identifier-only constructor payload grammar cannot contain an effect call. This does not evaluate a local value or grant authority. Calls are not presumed pure: a plain call can contain an effect-call argument and has no checked purity contract.

Theory conversion and integration executable-artifact requests now reject explicit authoritative/observational modes, check/observe/transfer expressions, linear aliases, and unclassified calls instead of silently erasing them. The local artifact request checks the strongest original AST before consuming its already projected communication view. Scope shadowing cannot hide a preceding authority binding from that whole-AST check. The generated-effects entry point also refuses these bindings, including those nested in control flow.

The table’s protocol-machine-executable classification describes the existing AST semantic surface, not proof that these bindings lower through a communication-only CodeImage. Positive effect-preserving authority lowering is still unavailable on that path. The supported Lean semantic-object theorems do not supply the missing compiler correspondence.

Explicit Local Binding Placement Prerequisite

A binding may spell its execution placement as let value at Coordinator = expr, with the same at Role clause after the binding name for authoritative and observational forms and for let ... in. The role reference must name a declared role. Indexed references retain the existing role-reference syntax. Placement is preserved in the AST, cloning and continuation composition. An omitted clause remains unplaced. It does not choose the first role or the next sender.

This is a source representation prerequisite, not executable authority lowering. An explicitly placed binding requires effect-preserving lowering even when its expression is plain. Communication theory conversion, direct local projection and generated-effects artifact construction refuse it. Generic communication programs and runtime.step keep their existing semantics. Placement does not issue a session capability, resolve a role family to an original endpoint, name a nominal operation at runtime, or authorize a continuation.

A future admitted nominal construct must bind this source placement to the actual coroutine’s session endpoint and generation, declared operation and argument/result types, then retain one issued continuation through its accepted outcome. The existing communication-only LocalTypeR and total Lean EffectRuntime.exec contract cannot express that transaction. They are unchanged by the placement prerequisite. Positive nominal execution and correspondence proof remain required before authority bindings can be admitted.

First Nominal Command Execution Contract

The selected-operation compiler admits an explicitly placed, declared Command with Unit input and Result String Bool output, followed immediately by an exhaustive Ok/Err case whose branches each send a distinct terminal label to the same peer. This is a command result, not authoritative evidence. Application Err, host Failure and a pending host outcome are distinct states.

The enclosing operation retains its source-selected NoAgreement profile, owner role and named progress window. That window requires an explicit nonzero tick budget. Missing configuration and unsupported agreement or escalation semantics refuse admission. Model ticks do not replace a host’s physical deadline.

An executable nominal call requires an actually issued owned-session handle, the original session generation and role endpoint, its exact admitted nominal declaration and a live canonical effect/operation occurrence. The coroutine retains the original nonserialized continuation and any returned typed result through commit gates. A pending response resumes that same occurrence and progress owner, without resetting the window. Admission and result commit require current_tick <= original_issued_tick + selected_budget, with checked overflow refused before issuance. Multi-window progress escalation cannot extend this original endpoint even while its canonical effect still reports Blocked. A changed semantic handler identity, stale generation, invalidated occurrence or restored checkpoint without that original handle cannot invoke the callback or consume the continuation. Handler identity is a semantic contract identifier. Equality does not prove physical provider identity or host-future teardown.

The compiler does not admit arbitrary expressions, authority-producing checks, readiness, publication, materialization, transfer or handoff by this command contract. Ordinary communication conversion continues to refuse explicitly placed authority expressions. Both backend producers and an executable nominal Lean transition are prepared source. Their fixtures and correspondence facts must be checked before any execution or parity claim. This is not full lowering of the remaining authority/evidence forms.

Executable Source Operation Continuations

begin, await, resolve, and invalidate describe one original operation continuation on its actual executing coroutine. begin does not spawn another runtime, thread or task, and does not invoke an unspecified host callback. It selects the declared operation, explicit owner role, agreement profile and original logical progress window. Its live continuation retains the actual issued session owner. Serialized declaration fields cannot reconstruct it.

A selected operation body retains the validated parent role scope and its original declaration. Its explicit owner must belong to that scope, and every referenced role must remain declared. An empty local operation need not invent communication for every parent role. Ordinary complete choreographies still require all declared roles to participate.

The selected source compiler binds its declared nominal callbacks as explicit children in the existing operation/effect/progress records. Communication and control instructions remain on the actual coroutine continuation. Incidental observational exchanges are not invented as additional declared child slots. A child is identified by its original canonical occurrence, not by a later effect name, last trace row, or a newly constructed equal-valued request. await observes this admitted set. A missing, ambiguous or pruned admitted child refuses. It never counts as terminal success. Live children keep the same operation and PC pending. An operation with no admitted children may pass this child-quiescence condition, but this neither executes omitted work nor proves any required dependent work was performed. Original unsuccessful child outcomes remain typed failures on the continuation.

resolve retains its proposed typed result before the final original-owner, window and child-completion gates. A refusal cannot substitute another result, reset the original window, or discard the original child failure. Duplicate resolution is rejected. An application Success status does not satisfy a selected proof requirement or create a publication, materialization proof or canonical handle. Those require their own genuine issuing/consuming transitions.

invalidate revokes the original semantic continuation and its still-live child occurrences. Late results cannot commit through that continuation. Invalidation is not host-future cancellation or physical resource settlement. Those resources and their actual completion obligations remain under the original host owner.

An executable handoff must transport this actual original continuation to an eligible target, including its child identities, selected original window, retained result and failure. Changing a session’s host owner, moving an endpoint, or emitting a semantic record alone does not execute handoff. Static role names may identify a destination but cannot replace the original issued owner gate.

Prepared authoritative readiness command slice

compile_command_operation additionally lowers an explicitly placed authoritative let result at A = check Host.ready() when the admitted nominal operation is authoritative, class : authoritative, progress : may_block, region : session, Unit input and Result String ReadinessWitness output. The existing explicit NoAgreement profile and selected named simulation-tick budget remain required. This bounded slice branches to terminal unit-message continuations. It does not implement witness transport, publication, materialization, or a general authority expression interpreter.

The callback returns a typed NominalCommandValue::Readiness, distinct from Bool. The predicate is the exact source interface and operation joined by .. Before executing the success continuation, both backends consume the original registered witness using the retained issued session owner, after output and WAL admission and destination validation. The registry checks exact issuance, session, owner, generation, full-session scope and single use. A Boolean, copied serialized field set, stale owner, wrong predicate, or foreign session cannot replace that check. Gate refusal keeps the original callback result resident and leaves the witness unconsumed. Transfer/rollback remains the canonical session transaction. This lowering adds no transfer registry.

These implementation and fixture declarations are source-only until their actual Rust harness discovery/execution and Lean elaboration succeed.

Prepared enclosing-operation executable slice

compile_operation_image selects the checked original operation body. The current enclosing slice supports an explicit Begin and either an empty body or one placed nominal Bool/readiness command with distinct terminal unit-message branches. Each child branch retains its explicit Await/Resolve tail. The static manifest binds each required child to the complete installed typed call and PC. Role-image assembly and slicing relocate both, while the loader verifies them against the actual installed opcode. Skipping required work cannot produce empty-inventory success. A Halt cannot abandon an unresolved original parent.

Begin, child invocation, result commit and explicit resolution retain one original issued owner and logical window. All nominal calls, including Bool commands, require that owner. Ordinary loading, checkpoint observations and a new registry generation cannot refresh a stale coroutine. Actual callback failure and independent output refusal retain the first typed execution fault and original returned outcome while revoking protocol commit. No revocation or application cancellation acknowledges physical host cleanup.

In this same-coroutine slice, a Blocked nominal child retains its original call PC until that child returns. This is not evidence of a separately pending Await. The existing scheduler has original-child wake semantics, but the genuine cross-continuation dependent-work producer, executable handoff, general cancellation/timeout escalation, stronger agreement, publication and materialization remain required implementation work. Unsupported forms refuse. That refusal does not complete those requirements. The prepared source and model examples require actual compilation and execution before acceptance.

Lean Verification

This document summarizes the current Lean implementation surface and the proof APIs consumed by runtime gates.

Source of Truth

Verification scale and proof-hole status are tracked by generated reports.

SourcePurpose
Lean Verification Code Mapgenerated library map with file counts and module inventory
just escapemachine check for axiom and sorry budget

These generated artifacts are the canonical sources for current scale and proof-hole status.

Library Layers

The Lean tree is organized as a layered stack.

LayerMain content
SessionTypesglobal and local syntax, de Bruijn forms, conversions
SessionCoTypescoinductive equality, bisimulation, decidable regular checks
Choreographyprojection, blindness, erasure, harmony
Semanticssmall-step semantics, determinism, deadlock surfaces
Protocolcoherence, typing, monitoring, deployment composition
Runtimeprotocol-machine model, semantics, runtime adapters, theorem-pack APIs
DistributedFLP, CAP, quorum, synchrony, Nakamoto, reconfiguration, safety families
Classicaltransported queueing and stochastic theorem families
ClassicalAnalysisInstancereal-analysis-backed concrete models used by classical transport
IrisExtractionInstanceruntime proof extraction and ghost logic bridge

Protocol-Machine Model and Runtime Surfaces

The protocol-machine model is centered under lean/Runtime/ProtocolMachine.

SurfaceLocation
Core instruction and state modelRuntime/ProtocolMachine/Model/*
Executable semanticsRuntime/ProtocolMachine/Semantics/*
Runtime adapters and monitorRuntime/ProtocolMachine/Runtime/*
Composition and domain instancesRuntime/ProtocolMachine/Composition.lean

The effect model uses the current split EffectRuntime and EffectSpec. Monitor typing lives in Runtime/ProtocolMachine/Runtime/Monitor.lean.

Heap Verification and Parity Surface

The runtime heap now has a focused Lean mirror for the contract that must stay stable across implementations.

SurfaceLocation
Canonical heap bytes, tagged preimages, ordering rules, replay interpreterRuntime/Resources/HeapModel.lean
Determinism lemmas for the executable heap modelRuntime/Proofs/Heap.lean
Executable heap parity runnerRuntime/Tests/HeapParityRunner.lean

This Lean heap lane is intentionally narrower than a full cryptographic model. Lean owns canonical encoding, tagged preimages, active/nullifier ordering, proof-index semantics, and deterministic replay for the published fixture corpus. The BLAKE3 digest function remains an operational assumption checked through the published heap vectors and the Rust↔Lean bridge suite.

Semantic Objects Model

The semantic objects layer lives under Runtime/ProtocolMachine/Model/SemanticObjects/.

SurfaceLocation
Identity, ownership, observed-read disciplineRuntime/ProtocolMachine/Model/SemanticObjects/Core.lean, Discipline.lean
Deferred-effect admissibility and stale late-result rejectionRuntime/ProtocolMachine/Model/SemanticObjects/OutstandingEffects.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/OutstandingEffects.lean
Semantic handoff activation and delegation bridgeRuntime/ProtocolMachine/Model/SemanticObjects/SemanticHandoffTransition.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/SemanticHandoff.lean
Authoritative-read commitment and publication projectionRuntime/ProtocolMachine/Model/SemanticObjects/AuthoritativeReadsPublication.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/AuthoritativeReadsPublication.lean
Materialization-proof adequacy and canonical-handle adequacyRuntime/ProtocolMachine/Model/SemanticObjects/MaterializationSuccess.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/MaterializationSuccess.lean
First-class capability/finalization and runtime-upgrade facadelean/Runtime/Proofs/CapabilityModel.lean, lean/Runtime/Tests/ProtocolMachineRunner.lean
Progress-contract semantics and escalation lemmasRuntime/ProtocolMachine/Model/SemanticObjects/ProgressContracts.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/ProgressContracts.lean
Transformation-local obligation bundlesRuntime/ProtocolMachine/Model/SemanticObjects/TransformationLocalObligations.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/TransformationLocalObligations.lean
Replay-failure exactnessRuntime/ProtocolMachine/Model/SemanticObjects/ReplayFailureExactness.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/ReplayFailureExactness.lean
Cross-target progress and dependent workRuntime/ProtocolMachine/Model/SemanticObjects/CrossTargetProgressDependentWork.lean, Runtime/Proofs/ProtocolMachine/SemanticObjects/CrossTargetProgressDependentWork.lean

Semantic-object theorem families attach to theorem-pack proof spaces through Runtime/Proofs/InvariantSpace.lean via SemanticObjectWitnessBundle.

Proof Packs and Admission APIs

Runtime proof inventory is exported through theorem-pack modules.

SurfacePurpose
Runtime/Proofs/TheoremPack/API.leanpublic theorem-pack facade and runtime gate aliases
Runtime/Proofs/TheoremPack/Inventory.leancapability inventory keys and determinism extension
Runtime/Proofs/TheoremPack/ReleaseConformance.leanrelease gate and replay conformance report
Runtime/Adequacy/EnvelopeCore/AdmissionLogic.leanadmission soundness, completeness, diagnostics vocabulary

These APIs are consumed by Rust runtime gates and composition admission checks.

The capability/finalization bridge surface is also consumed directly by the strict correspondence lane through inspectCapabilityModel, which keeps the Lean model for finalization paths and runtime-upgrade artifacts in executable alignment with Rust.

Premise-Scoped Interfaces

Some surfaces are intentionally premise-scoped.

Interface classExample
Threaded refinement beyond n = 1ThreadedRoundRefinementPremises
Envelope admission and profile diagnosticsadmission protocol structures in AdmissionLogic.lean
Mixed-profile runtime gatestheorem-pack and runtime-contract capability checks

These are explicit interfaces and not unconditional global theorems.

Distributed and Classical Reach

Distributed and classical families are part of the active theorem-pack space. They are not side notes.

Distributed families include FLP, CAP, quorum geometry, partial synchrony, responsiveness, Nakamoto security, reconfiguration, atomic broadcast, accountable safety, failure detectors, data availability, coordination, CRDT, Byzantine safety, consensus envelope, and failure envelope.

Classical transport families include Foster-Lyapunov, MaxWeight, large deviations, mean-field, heavy-traffic, mixing, fluid limits, concentration bounds, Little’s law, functional CLT, and spectral-gap termination. The runtime proof-pack path is Classical.Profiles / Protocol.Classical constructors, Runtime.Proofs.Adapters.Classical profiles, ProtocolMachineInvariantSpaceWithProfiles setters, and buildProtocolMachineTheoremPack artifacts. These families are honest conditional interfaces: the named classical theorems are assumed inputs carried by witness fields (or the family proves only an elementary identity), not Lean proofs. See classicalEvidenceInventory and Theorem Pack Inputs.

Real-analysis facts used by these families remain explicit behind the ClassicalAnalysisAPI.lean and ClassicalAnalysisInstance.lean trust boundary. The Classical proof audit rejects new theorem-shaped assumptions outside named API/Instance boundaries.

Runtime Alignment Lanes

Lean and Rust alignment is checked by automation lanes.

LaneCommand
Runtime capability gatesjust check-capability-gates
Release conformancejust check-release-conformance
Protocol-machine parity suitejust check-parity --suite
Type and schema parityjust check-parity --types
Conformance-specific parity lanejust check-parity --conformance
Consolidated parity lanejust check-parity --all
Heap parity lanecargo test -p telltale-bridge --test heap_lean_parity

| rust-communication-hash-model | telltale-machine runtime maintainers | Active, runtime-only | Lean models communication nullifiers and replay roots as injective reprStr preimages; Rust digests the same preimage with the configured HashModel | Hash choice changes digest bytes only, not acceptance: both sides reject exactly the repeated preimages for the selected CommunicationNullifierIdentity; default model digests are unchanged | Modeling a concrete hash in Lean would add a collision assumption without strengthening the replay contract | 2027-04-08 | Rust unit and engine regressions cover default digest stability, custom-model determinism, content-only duplicate rejection and fail-closed deserialization; Lean runtime tests 41a and 41b cover both identity scopes | | CommunicationNullifierIdentity variants (sequence_bound, content_only) | Runtime/ProtocolMachine/Model/Config.lean, Runtime/ProtocolMachine/Model/State.lean | rust/machine/src/communication_replay/identity.rs | Aligned |

Rust-Lean Bridge and Parity

This document defines the typed Rust<->Lean bridge surface and the parity contract enforced across that boundary for protocol-machine behavior, choreography projection, semantic-object schemas, and deviation governance.

Contract Levels

Parity is enforced at two levels. Level 1 covers policy and data shape parity for shared runtime encodings. Level 2 covers behavior parity for executable traces under the declared concurrency envelope.

Bridge Scope

The telltale-bridge crate is the typed boundary between Rust artifacts and Lean validation entrypoints. It handles JSON conversion, schema versioning, runner invocation, trace comparison, semantic-object export, and typed invariant bundle export.

The bridge does not define protocol-machine semantics. Semantics remain in telltale-machine, telltale-theory, and Lean runtime modules. Host-runtime handlers also remain outside the bridge and re-enter through typed effect surfaces.

The same reduced-fixture parity style also applies to telltale-search for the concurrency-critical slice. That lane covers:

  • canonical min-key batch extraction
  • proposal independence over declared authority surfaces
  • replay epoch and phase contracts
  • epoch-barrier transitions
  • fairness-bundle fixture alignment

It is intentionally reduced. It does not attempt to prove full ARA* optimality or every scheduler policy.

This document covers the bridge behavior implemented in rust/bridge/src:

  • export
  • import
  • schema
  • runner
  • protocol_machine_runner
  • heap_parity_runner
  • sim_reference
  • semantic_objects
  • protocol_machine_trace
  • invariants
  • equivalence
  • validate

Bridge Crate Surface

The canonical public bridge surface uses:

  • LeanRunner
  • ProtocolMachineRunner
  • HeapParityRunner
  • ProtocolMachineRunInput
  • ProtocolMachineRunOutput
  • HeapParityOutput
  • ProtocolMachineSemanticObjects

No public legacy compatibility modules are part of the supported bridge story.

Features and Binaries

The crate has feature-gated modules and binaries. The default feature set enables Lean runner integration.

[features]
default = ["runner"]
cli = ["clap"]
runner = ["telltale-theory"]
exporter = ["telltale-runtime", "anyhow", "bpaf"]
golden = ["clap", "anyhow", "runner"]

[[bin]]
name = "lean-bridge"
required-features = ["cli"]

[[bin]]
name = "lean-bridge-exporter"
required-features = ["exporter"]

[[bin]]
name = "golden"
required-features = ["golden"]

runner controls LeanRunner, ProtocolMachineRunner, HeapParityRunner, and the equivalence modules. Without runner, conversion and schema modules remain available.

Conversion and Schema Families

export::global_to_json() and export::local_to_json() map Rust protocol types into Lean-compatible JSON. import::json_to_global() and import::json_to_local() parse those JSON forms back into GlobalType and LocalTypeR.

The bridge defines three main schema families with explicit version constants:

FamilyVersion ConstantValuePrimary Payloads
Lean bridge coreLEAN_BRIDGE_SCHEMA_VERSIONlean_bridge.v1ProtocolMachineRunInput, ProtocolMachineRunOutput, SimRunInput, SimRunOutput, replay bundles
Protocol bundlesPROTOCOL_BUNDLE_SCHEMA_VERSIONprotocol_bundle.v1ProtocolBundle
Protocol-machine semantic objectsSEMANTIC_OBJECTS_SCHEMA_VERSIONprotocol_machine.semantic_objects.v1ProtocolMachineSemanticObjects export payloads

schema::ensure_supported_schema_version() rejects unsupported bridge-core versions. Unsupported or missing schema versions are rejected rather than normalized.

Lean Runners

LeanRunner invokes the validator binary at lean/.lake/build/bin/telltale_validator. It supports validation and projection export workflows.

ProtocolMachineRunner invokes lean/.lake/build/bin/protocol_machine_runner. It serializes ProtocolMachineRunInput to stdin and parses ProtocolMachineRunOutput from stdout. It also exposes validate_trace(), run_reference_simulation(), validate_simulation_trace(), verify_invariants(), and compare_execution().

HeapParityRunner invokes lean/.lake/build/bin/heap_parity_runner. It loads the published heap parity corpus and returns typed HeapParityOutput values for strict Rust<->Lean comparison.

Semantic Objects and Trace Normalization

semantic_objects provides the canonical bridge payloads for the protocol-machine semantic-object family. ProtocolMachineRunOutput.semantic_objects and ProtocolMachineRunOutput.effect_exchanges are the canonical bridge-side exports for semantic runtime state and typed effect exchange data.

protocol_machine_trace::event_session() extracts session ids from common event shapes. normalize_semantic_audit() rewrites tick values into per-session local counters. semantic_audits_equivalent() and observationally_equivalent() compare traces after that normalization.

Invariant, Equivalence, and Validator Utilities

invariants defines typed claim schemas for Lean-side protocol bundle verification through ProtocolBundle. equivalence::EquivalenceChecker supports both golden-file comparison and live Lean comparison. validate::Validator provides lightweight checks for projection, roundtrip, and subtype-related workflows that do not require full protocol-machine execution.

Bridge Test and Tooling Lanes

Bridge tests in rust/bridge/tests cover conversion, projection parity, schema compatibility, invariant verification, protocol-machine correspondence, and heap parity. Examples include:

  • lean_integration_tests.rs
  • lean_trace_validation.rs
  • projection_runner_tests.rs
  • protocol_machine_correspondence_tests.rs
  • protocol_machine_differential_steps.rs
  • heap_lean_parity.rs
  • capability_model_correspondence.rs
  • protocol_bundle_admission_contracts.rs

These lanes align with repository-level gates such as just check-parity --types, just check-parity --suite, and just check-capability-gates.

Protocol-Machine Policy and Data Shapes

The following shapes must remain aligned between Lean and Rust unless a deviation entry is active.

AreaLean SurfaceRust SurfaceStatus
FlowPolicy variantsRuntime/ProtocolMachine/Model/Kledge.leanrust/machine/src/engine/Aligned
FlowPredicate variantsRuntime/ProtocolMachine/Model/Kledge.leanrust/machine/src/engine/Aligned
OutputConditionPolicyRuntime/ProtocolMachine/Model/OutputCondition.leanrust/machine/src/output_condition.rsAligned
Capability/finalization model (FinalizationPath, FinalizationReadClass, FinalizationStage, RuntimeUpgradeArtifact)lean/Runtime/Proofs/CapabilityModel.lean, Runtime/Tests/ProtocolMachineRunner.leanrust/machine/src/semantic_objects.rs, rust/machine/src/composition.rs, rust/machine/src/capabilities.rsAligned
Runtime Value variantsProtocol/Values.leanrust/machine/src/coroutine.rsAligned
ProgressToken fieldsRuntime/ProtocolMachine/Model/State.leanrust/machine/src/coroutine.rsAligned
CommunicationReplayMode variantsRuntime/ProtocolMachine/Model/Config.leanrust/machine/src/communication_replay/mod.rsAligned
SignedValue transport fields (payload, signature, sequence_no)Runtime/ProtocolMachine/Model/TypeClasses.leanrust/machine/src/buffer.rsAligned
Payload hardening controls (payload_validation_mode, max_payload_bytes)Runtime/ProtocolMachine/Model/Config.lean, Runtime/ProtocolMachine/Semantics/ExecComm.leanrust/machine/src/engine/Aligned
Register bounds failure semantics (OutOfRegisters)Runtime/ProtocolMachine/Semantics/ExecSteps.leanrust/machine/src/engine/, rust/machine/src/threaded/Aligned
Explicit failure/timeout observable event inventory (TimeoutIssued, CancellationRequested, Cancelled, FailureBranchEntered, SessionTerminal)Runtime/ProtocolMachine/Model/State.lean, Runtime/ProtocolMachine/Runtime/Json.lean, Runtime/Proofs/TheoremPack/ReleaseConformance.leanrust/machine/src/engine/protocol_machine_config.rs, rust/machine/src/trace.rsAligned

These checks are automated by just check-parity --types.

Protocol-Machine Behavior Contract

RegimeRequired Behavior
Canonical n = 1Exact parity between cooperative and threaded execution
Threaded n > 1Conformance within declared EnvelopeDiff bounds
Failure-visible artifactsSnapshot parity within declared failure envelope class
SpeculationNo sentinel fallback behavior for join/abort with deterministic gated semantics
Register boundsOut-of-range register operands fail with OutOfRegisters (no unchecked panic paths)
Load boundaryRuntime rejects malformed trusted image role/type shape before session open
Explicit failure and timeout orderingper-session trace preserves TimeoutIssued, CancellationRequested, Cancelled, FailureBranchEntered, and SessionTerminal ordering
Host targeted retirementCooperative and threaded disposal share the required retirement contract; Lean host-lifecycle deviation is recorded as rust-host-targeted-disposal

These checks are automated by just check-parity --suite.

Explicit failure, timeout, cancellation, and session-terminal events are part of the executable Lean runtime/event inventory. Replay tagging, JSON serialization, and theorem-pack release conformance all use the same observable event family.

Language-level nominal effect declarations do not introduce a second runtime bridge. Their intended justification remains the existing protocol-machine invoke boundary and handler-typing obligations in Runtime/Proofs/protocol machine/BridgeStrengthening.lean.

Typed effect requests and outcomes are part of the parity surface directly. Rust and Lean must agree on effect-interface metadata, request bodies, outcome statuses, and replay-visible effect exchanges. Thatincludes the internal wal_sync effect metadata and the shared durable recovery vocabulary used to classify checkpoint-plus-WAL resume decisions.

Effect Interface Justification

The language-level effect interface design follows the same proof boundary used by the MPST-plus-effects papers and the Lean runtime.

  • typed effect obligations remain attached to the executable handler boundary, not to a separate host-only mechanism
  • authority-sensitive language constructs must lower to explicit effect observations, witness issuance/consumption, and failure-visible events
  • proof-carrying evidence remains fail-closed on missing or invalid authority
  • behavioral correspondence is observational, through traces and effect/audit surfaces, rather than through arbitrary host internals

This is why the current language design is nominal-first:

  • effect declarations and uses clauses are stable names for explicit host obligations
  • lowering stays centered on the existing protocol-machine invoke and EffectSpec story
  • generalized effect polymorphism waits until the nominal surface, lowering, and parity/audit semantics are stable

Durability Alignment

The durability layer is part of the documented Rust<->Lean correspondence boundary for runtime effects.

  • Rust EffectRequestBody::WalSync corresponds to Lean walSyncMetadata plus the shared EffectRequestBody / EffectResponse effect model in lean/Runtime/ProtocolMachine/Model/Effects.lean
  • Rust DurableRecoveryAction / DurableRecoveryDecision correspond to Lean Runtime.ProtocolMachine.Model.DurableRecoveryAction / DurableRecoveryDecision
  • The current Lean theorem surface for this layer is intentionally narrow: walSyncMetadata_legal, internal-handler admissibility, recovery-rank monotonicity from AgreementEscalation, and gate-crossing / terminal-truth consequences from DurableRecoveryDecision.sound

The simulator-side WAL backend wrappers and fault-injection helpers remain Rust assurance surfaces. They consume the typed durability vocabulary above but are not themselves part of the current mechanized claim.

Choreography Projection Parity

The parity scope covers projection behavior from global choreography forms to local session forms. This includes send, choice, recursion, and merge behavior for non-participant branches. Rust-only runtime conveniences and extension-only AST constructs are excluded.

Shared Projection Semantics

Rust and Lean are expected to align on the following surfaces.

AreaLean SurfaceRust SurfaceStatus
Projection core relationlean/Choreography/Projection/Project.leanrust/runtime/src/compiler/projection.rsAligned on supported subset
Merge semanticslean/Choreography/Projection/Erasure/Merge.leanrust/language/src/compiler/projection/merge.rsAligned
Projection validation pipelinelean/Choreography/Projection/Validator.leanrust/bridge/src/runner_projection_export.rsAligned

Rust-Only Extensions

The following surfaces are intentionally outside direct Lean parity. They must be documented as extensions and must not be confused with theorem-backed projection claims.

SurfaceRust ModuleParity Status
LocalType::LocalChoicerust/language/src/ast/local_type.rsRust extension
Timeout wrappers in local ASTrust/language/src/ast/local_type.rsRust extension
Effect runtime Parallel execution contractrust/runtime/src/effects/interpreter.rsRust runtime contract

Projection Cross-Validation

Projection cross-validation is exercised through rust/bridge/tests/projection_runner_tests.rs. Tests skip per test when the Lean validator binary is unavailable. Skipping one test must not terminate the rest of the suite.

Heap Parity Surface

The runtime heap has a focused Lean mirror for the parts of the contract that must remain cross-implementation stable.

SurfaceLean SurfaceRust SurfaceStatus
Canonical resource bytes and tagged preimageslean/Runtime/Resources/HeapModel.leanrust/runtime/src/heap/encoding.rs, rust/runtime/src/heap/resource.rsAligned
Active/nullifier ordering and replay interpreterlean/Runtime/Resources/HeapModel.lean, lean/Runtime/Proofs/Heap.leanrust/runtime/src/heap/heap_impl.rsAligned
Proof-index and sibling-direction semanticslean/Runtime/Resources/HeapModel.leanrust/runtime/src/heap/merkle.rsAligned
Published heap parity corpuslean/Runtime/Tests/HeapParityRunner.leanrust/runtime/tests/data/heap_lean_parity_v1.json, rust/bridge/tests/heap_lean_parity.rsAligned

This heap lane is narrower than a full cryptographic equivalence proof. Lean does not currently implement BLAKE3. Instead, Lean reconstructs canonical bytes, tagged preimages, ordering, proof-path structure, and deterministic replay from the published corpus, and Rust must match the published digest values exactly.

The authoritative digest corpus remains the published heap vector set documented in Resource Heap. rust/runtime/tests/data/heap_vectors_v1.json remains the minimal public digest vector file. rust/runtime/tests/data/heap_lean_parity_v1.json is the richer Rust<->Lean parity corpus.

Heap Enforcement

The Lean heap surface is split into three files:

SurfaceLocation
Executable heap modellean/Runtime/Resources/HeapModel.lean
Basic determinism lemmaslean/Runtime/Proofs/Heap.lean
Executable parity runnerlean/Runtime/Tests/HeapParityRunner.lean

The strict Rust<->Lean heap suite is rust/bridge/tests/heap_lean_parity.rs. It runs the Lean executable, recomputes the same values in Rust through public heap APIs, and checks that both sides match the published corpus exactly.

This lane justifies a stronger statement than “heap behavior is only tested in Rust,” but it is still not a full theorem-backed digest proof.

Current claim:

  • Lean and Rust agree on canonical heap bytes, tagged preimages, ordering, proof-path structure, and deterministic replay for the published heap corpus.
  • Rust produces the authoritative digest values, and those values must match the published vectors exactly.

Not yet claimed:

  • a pure Lean implementation of BLAKE3
  • a theorem that the Rust digest implementation is cryptographically correct
  • full mechanized heap refinement against all runtime heap code paths

State Schema

Lean and Rust schemas remain shape-equivalent on safety-visible and replay-visible fields. Runtime-only helper fields are permitted when they do not alter observable semantics.

Semantic Object Family

The canonical cross-language semantic-object family must remain aligned between Lean, Rust, and the Rust/Lean bridge.

ObjectLean SurfaceRust SurfaceBridge SurfaceStatus
OperationInstanceRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
OutstandingEffectRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
SemanticHandoffRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
TransformationObligationRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
AuthoritativeRead / ObservedReadRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
MaterializationProof / CanonicalHandleRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
ProgressContractRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
ProgressTransitionRuntime/ProtocolMachine/Model/SemanticObjects/Core.leanrust/machine/src/semantic_objects.rsrust/bridge/src/semantic_objects.rsAligned
typed effect metadata / request / outcome modelRuntime/ProtocolMachine/Model/Effects.leanrust/machine/src/effect.rsrust/bridge/src/protocol_machine_runner.rs (effect_exchanges)Aligned

OperationInstance and OutstandingEffect are compared as canonical runtime state, not as post-hoc derivations from generic effect-trace order. Parity on these objects covers owner identity, phase/status, budget/invalidation fields, dependent-operation edges, and terminal publication state.

SemanticHandoff parity also covers explicit owner revocation and activation (revoked_owner_id, activated_owner_id). TransformationObligation parity covers transformed fragments, affected operations/effects, transport vs invalidation policy, and publication transfer or revocation. Bridge-side execution comparison reports these handoff and invalidation surfaces separately from raw trace equivalence. This prevents stale-owner and late-result mismatches from hiding inside otherwise equivalent instruction traces.

The same strict bridge layer also compares the first-class capability/finalization/transition facade exported by inspectCapabilityModel. That lane checks canonical vs invalidated finalization paths and committed-cutover vs rolled-back runtime-upgrade artifacts directly, instead of treating them as documentation-only structure.

Canonical layout mapping for this family is:

  • Lean executable spec: lean/Runtime/ProtocolMachine/Model/SemanticObjects/*.lean
  • Rust runtime mirror: rust/machine/src/semantic_objects.rs
  • Rust bridge mirror: rust/bridge/src/semantic_objects.rs This bridge surface must re-export the machine semantic-object family rather than carrying a duplicate schema copy.

Region is part of the canonical protocol-machine semantic-object family. It names the session-scoped locality and framing domain over operations, effects, authoritative reads, canonical handles, and publication events.

ProgressContract parity covers bounded-wait metadata, explicit no-progress and degraded states, and timeout escalation state. ProgressTransition parity makes those escalations replay-visible instead of leaving them as target-specific scheduling heuristics.

The Lean implementation layer keeps executable semantic-object definitions in Runtime/ProtocolMachine/Model/SemanticObjects/Core.lean. Basic theorem-facing predicates live in Runtime/ProtocolMachine/Model/SemanticObjects/Discipline.lean. The re-export facade is Runtime/ProtocolMachine/Model/SemanticObjects.lean.

Deferred-effect admissibility, retry shape, and late-result rejection live in Runtime/ProtocolMachine/Model/SemanticObjects/OutstandingEffects.lean. Associated theorem-facing lemmas are in Runtime/Proofs/ProtocolMachine/SemanticObjects/OutstandingEffects.lean.

Semantic handoff realization lives in Runtime/ProtocolMachine/Model/SemanticObjects/SemanticHandoffTransition.lean. Theorem-facing owner/publication/delegation bridge lemmas are in Runtime/Proofs/ProtocolMachine/SemanticObjects/SemanticHandoff.lean.

Authoritative-read commitment contexts and canonical publication-path uniqueness live in Runtime/ProtocolMachine/Model/SemanticObjects/AuthoritativeReadsPublication.lean. Observer-projection, blindness, and noninterference lemmas are in Runtime/Proofs/ProtocolMachine/SemanticObjects/AuthoritativeReadsPublication.lean.

Proof-backed success contexts and materialization-proof adequacy live in Runtime/ProtocolMachine/Model/SemanticObjects/MaterializationSuccess.lean. Lemmas ruling out proof-less success and observational materialization promotion are in Runtime/Proofs/ProtocolMachine/SemanticObjects/MaterializationSuccess.lean.

Progress-contract semantics live in Runtime/ProtocolMachine/Model/SemanticObjects/ProgressContracts.lean. Owner-liveness, escalation, and Lyapunov/weighted-measure/scheduling-bound compatibility lemmas are in Runtime/Proofs/ProtocolMachine/SemanticObjects/ProgressContracts.lean.

Transformation-local obligation bundles live in Runtime/ProtocolMachine/Model/SemanticObjects/TransformationLocalObligations.lean. Coverage/admissibility lemmas and lightweight linking/reconfiguration bridge structures are in Runtime/Proofs/ProtocolMachine/SemanticObjects/TransformationLocalObligations.lean.

Theorem-pack attachment for these semantic-object proof families lives in Runtime/Proofs/InvariantSpace.lean via SemanticObjectWitnessBundle. The same attachment points are exposed through Runtime/Proofs/TheoremPack/Inventory.lean, Runtime/Proofs/TheoremPack/API.lean, and Runtime/Proofs/Contracts/RuntimeContracts.lean.

Lean ProtocolMachineState

Source: lean/Runtime/ProtocolMachine/Model/State.lean

CoroutineState contains id, programId, pc, regs, status, effectCtx, ownedEndpoints, progressTokens, kledgeSet, costBudget, and specState.

The Lean protocol-machine state structure (ProtocolMachineState) contains config, programs, coroutines, sessions, monitor, sched, resourceStates, persistent, obsTrace, failure/topology state fields, and output-condition state.

Rust Protocol Machine

Source: rust/machine/src/engine/

The Rust protocol-machine structure (ProtocolMachine, exported as an alias for protocol machine) contains config, programs, code, coroutines, sessions, monitor, sched, resource_states, persistent, obs_trace, symbol/clock counters, failure/topology state fields, and output-condition state.

Coroutine in rust/machine/src/coroutine.rs contains identity/program/pc/status, register file, ownership/progress/kledge sets, cost budget, speculation metadata, and effect context.

Canonical Rust Runtime Object Inventory

The Rust public runtime surface exposes one canonical naming scheme: protocol-machine objects use ProtocolMachine*, guest-runtime objects use GuestRuntime*, and bridge execution objects use ProtocolMachineRunner*. No public telltale_machine::vm::*, telltale_machine::threaded::*, telltale_bridge::protocol_machine_runner::*, or telltale_bridge::vm_trace::* entrypoints remain.

Runtime ObjectLean SurfaceRust SurfaceBridge SurfaceStatus
protocol-machine configRuntime/ProtocolMachine/Model/Config.leantelltale_machine::ProtocolMachineConfigtelltale_bridge::ProtocolMachineRunInputAligned
protocol-machine stateRuntime/ProtocolMachine/Model/State.leantelltale_machine::ProtocolMachineStatetelltale_bridge::ProtocolMachineRunOutputAligned
protocol-machine executorRuntime/ProtocolMachine/API.lean, Runtime/ProtocolMachine/Runtime/Runner.leantelltale_machine::ProtocolMachinetelltale_bridge::ProtocolMachineRunnerAligned
protocol-machine step resultRuntime/ProtocolMachine/Model/State.leantelltale_machine::StepResulttelltale_bridge::ProtocolMachineStepStateAligned
protocol-machine run statusRuntime/ProtocolMachine/Model/State.leantelltale_machine::RunStatustelltale_bridge::ProtocolMachineRunOutput.statusAligned
protocol-machine error surfaceRuntime/ProtocolMachine/Model/State.lean, Runtime/ProtocolMachine/Runtime/Json.leantelltale_machine::ProtocolMachineErrortelltale_bridge::LeanStructuredErrorAligned
protocol-machine memory accountingRuntime/ProtocolMachine/Model/State.leantelltale_machine::ProtocolMachineMemoryUsage, telltale_machine::ProtocolMachineRetainedBytesn/aAligned
guest runtime driverRuntime/ProtocolMachine/API.leantelltale_machine::GuestRuntime, telltale_machine::ThreadedGuestRuntimen/aAligned
threaded protocol-machine adapterRuntime/ProtocolMachine/API.lean, Runtime/ProtocolMachine/Composition.leantelltale_machine::ThreadedProtocolMachineparity tests under rust/bridge/tests/protocol_machine_cross_target_tests.rsAligned
semantic-object inventoryRuntime/ProtocolMachine/Model/SemanticObjects*.leantelltale_machine::{ProtocolMachineSemanticObjects, OperationInstance, OutstandingEffect, SemanticHandoff, TransformationObligation, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, PublicationEvent, Region, ProgressContract, ProgressTransition}telltale_bridge::{ProtocolMachineSemanticObjects, OperationInstance, OutstandingEffect, SemanticHandoff, TransformationObligation, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, PublicationEvent, Region, ProgressContract, ProgressTransition}Aligned
runtime admission contractsRuntime/Proofs/Contracts/RuntimeContracts.leantelltale_machine::{requires_protocol_machine_runtime_contracts, admit_protocol_machine_runtime, enforce_protocol_machine_runtime_gates, request_determinism_profile, runtime_capability_snapshot}n/aAligned

Runtime Capability Gates

Runtime modes that require theorem/capability evidence are admission gated.

Gate SurfaceLean APIRust APIStatus
Advanced mode admissionrequiresVMRuntimeContracts, admitVMRuntimerequires_protocol_machine_runtime_contracts, admit_protocol_machine_runtimeAligned
Determinism profile validationrequestDeterminismProfilerequest_determinism_profileAligned
Runtime capability snapshotruntimeCapabilitySnapshotruntime_capability_snapshotAligned

The Rust surfaces are in rust/machine/src/runtime_contracts.rs and rust/machine/src/composition.rs. On the Lean side, TheoremPackCapabilityContract.semanticAttachmentPoints provides the canonical runtime-facing list of enabled semantic-object theorem attachment points.

Determinism Profiles

ProfileLeanRustStatus
FullfullDeterminismMode::FullAligned
Modulo effect tracemoduloEffectTraceDeterminismMode::ModuloEffectsAligned
Modulo commutativitymoduloCommutativityDeterminismMode::ModuloCommutativityAligned
ReplayreplayDeterminismMode::ReplayAligned

Full is exact-trace mode. ModuloEffects and ModuloCommutativity require mixed-profile capability evidence and artifact support. Replay requires replay artifact support and mixed-profile capability evidence.

Threaded Concurrency Envelope

RegimeLean SurfaceRust SurfaceStatus
n = 1 exact refinementrunScheduledThreaded_one_eq_runScheduledthreaded_equivalence.rs::test_threaded_matches_cooperativeAligned
Spawn step parity (n = 1)Runtime/ProtocolMachine/Semantics/ExecControl.lean, Runtime/ProtocolMachine/Semantics/ExecSteps.leandifferential_step_corpus.rs::threaded_matches_cooperative_step_corpus_control_spawnAligned
Certified-wave fallbackexecuteCertifiedRoundthreaded.rs wave certificate check with one-step fallbackAligned
n > 1 envelope-bounded parityThreadedRoundRefinementPremises (premise-scoped)parity_fixtures_v2.rs::envelope_bounded_parity_holds_for_n_gt_1Aligned under envelope contract

Simulator Field Mirror

Lean includes executable mirror dynamics for simulator field handlers under lean/Runtime/Simulation/. Rust field handlers live under rust/simulator/src/field_handlers/. The mirror includes a Lean-native field-model boundary in lean/Runtime/Simulation/Field.lean, including built-in catalog dispatch and default initial-state derivation for shipped field families. It remains an executable parity layer, not a mirror of Rust trait objects or serde-based scenario parsing.

Parity fixtures are enforced by:

  • rust/simulator/tests/field_handler_parity.rs
  • lean/Runtime/Tests/SimulatorParity.lean (built as simulator_parity_tests)

Reduced Semantic-Effect Parity

Lean also publishes a reduced semantic-effect fixture surface for the protocol-machine/runtime boundary. This lane is narrower than full executable protocol-machine equivalence: it checks the semantic classification we rely on for effect-facing debugging and replay exactness without claiming that Lean executes the full Rust effect handler stack.

The reduced surface covers:

  • effect kind
  • lifecycle classification (blocked / succeeded)
  • interface and operation naming
  • output-condition predicate material visible to publications
  • whether the successful send path materializes an authoritative publication handle

Parity fixtures are enforced by:

  • lean/Runtime/Tests/SemanticEffectParity.lean (built as semantic_effect_parity_runner)
  • rust/machine/tests/semantic_effect_lean.rs
  • rust/simulator/tests/semantic_effect_lean.rs

The machine and simulator tests compare canonical Rust outputs against the Lean fixture bundle for:

  • one blocked-send fixture
  • one successful send/publication fixture
  • one output_condition_hint fixture
  • one wal_sync fixture

This lane is intended to keep Lean, Rust machine semantics, replay/runtime behavior, and simulator artifact extraction aligned on the effect-visible semantic slice that matters for time-travel debugging and exact replay.

Lean Module Boundaries

The lean/Runtime/ProtocolMachine directory is organized into executable and proof modules.

The Runtime/ProtocolMachine/Model/ directory contains runtime data types, config, state, instruction forms, and event surfaces. The Runtime/ProtocolMachine/Semantics/ directory contains executable step semantics. The Runtime/ProtocolMachine/Runtime/ directory contains runtime adapters for loading, JSON, monitoring, and failure ingress.

The Runtime/ProtocolMachine/API.lean file provides the stable facade for executable protocol-machine API imports. The Runtime/ProtocolMachine/Composition.lean file contains composition/admission and theorem-pack integration surfaces. The Runtime/Proofs/ directory contains proof/theorem-pack modules not required for core executable stepping.

Executable modules must not depend on placeholder proof definitions. Proof-only placeholders stay isolated under proof modules. Any executable-path dependency on a stub or placeholder is a release blocker.

Deviation Governance

Any intentional parity break must be recorded in the deviation table below before merge. Required fields include id, owner, status, reason, impact, alternatives considered, revisit date, and coverage scope.

Deviation Registry

IdOwnerStatusReasonImpactAlternatives consideredRevisit dateCoverage scope
rust-host-targeted-disposaltelltale-machine runtime maintainersActive, runtime-onlyExclusive host disposal and scoped Rayon worker acknowledgment concern Rust execution custody outside the Lean small-step programAdds targeted retirement APIs and typed preflight errors; no shared wire schema, instruction or capability shape changes; host retirement does not establish protocol successStatus-only close leaves runnable custody; whole-machine drop removes unrelated sessions2027-01-03Cooperative and threaded required-reap regressions cover surviving stable ids, actual worker acknowledgment, preflight faults, natural terminal epochs, repeated acknowledgment and cooperative deserialization; cross-backend disposal summary regression covers the common retirement contract
rust-host-worker-pool-error-sourcetelltale-machine runtime maintainersActive, runtime-onlyNative worker construction and its original I/O cause are outside Lean protocol state and small-step executionThreadPoolBuild retains the actual Rayon error chain; diagnostic display is unchanged; no instruction, capability, wire format or successful protocol trace changes; constructor refusal supplies no worker-termination ACKStringifying native causes or treating constructor failure and pool Drop as acknowledged teardown2027-01-20Two native multi-thread fixtures declare first-spawn refusal and later-spawn refusal after an actual first-worker acknowledgment; the latter retains and joins original native handles before assertions; these tests do not establish production pool-join capability
rust-host-owned-session-paritytelltale-machine runtime maintainersActive, runtime-onlyHost session mutation, staged ownership receipt validation and original Rust error sources are embedding contracts outside endpoint/coroutine small-step transferBoth backends share resident-state ownership transitions; owned handles validate receipt/session association and current generation; transfer/attenuation cannot widen scope; release/reclaim generations remain monotonic; exhausted counters fail before mutation; the canonical journal preserves actual cross-session emission order and machine ticks across disposal; cooperative snapshots add a defaulted ordered journal and last-generation storage; OwnershipContract carries a typed sourceDuplicated threaded ownership logic, bare-store mutation, saturating/reused authority ids, stringified causes, or reconstructing authority from audit records2027-01-19owned_backend_parity exercises both backends, claim-specific failed commit/rollback, foreign receipt refusal, witness consumption/rejection, stale and scoped handles, terminal ordering, typed causes and retained audit; ownership_preflight_tests covers release/reclaim restoration, scope escalation, counter exhaustion before terminal mutation and foreign edges; ownership_lock_tests exercises the original poisoned custody and audit locks before mutation

The Lean Runtime/ProtocolMachine/Model/State.lean session state and Runtime/ProtocolMachine/Semantics/ExecSteps.lean execution surfaces remain the reference for protocol steps. Targeted host disposal is not a new instruction, transition witness, or claim that Lean proves Rayon worker termination. Rust’s exclusive owner and joined worker scope enforce that runtime obligation.

conservative-async-subtyping-contract

Conservative async-subtyping (Lean and Rust) is intentionally phase- and tree-structural:

  • SISO decomposition must succeed on both sides under bounded unfolding.
  • Subtype and supertype must have equal phase counts.
  • Each aligned phase must satisfy input-tree and output-tree structural compatibility.
  • Empty behavior (End) only matches supertypes with empty phase decomposition under this conservative contract.

CI Gates

The minimum parity governance gates are just check-parity --all, just check-release-conformance, and workflow gates in .github/workflows/verify.yml and .github/workflows/check.yml.

If any gate fails, parity drift is treated as a release blocker.

Performance SLA

Runtime performance governance enforces explicit thresholds from artifacts/v2/benchmark_matrix/summary.json through just perf-baseline sla.

  • TT_SLA_THROUGHPUT_RATIO_MIN (default 1.0)
  • TT_SLA_P99_REGRESSION_MAX_PERCENT (default 15.0)
  • TT_SLA_LOCK_CONTENTION_REDUCTION_MIN_PERCENT (default 50.0)

If any threshold is violated, CI fails before benchmark lanes are considered healthy.

Update Rules

When any parity matrix row changes, update the Deviation Registry table in this file in the same change set. For any protocol-machine PR that changes public runtime behavior, include a parity impact statement in the PR checklist. Add differential tests when observable behavior changes.

Any Rust PR that changes projection or merge semantics must include:

  1. The affected Rust module list.
  2. The Lean module list reviewed for parity.
  3. New or updated cross-validation tests for the changed behavior.
  4. A parity note update in this document when scope or status changes.

Naming Surface

Rust protocol-machine code uses one canonical snake_case naming surface. Lean-specific casing remains on the Lean side. Rust APIs should not preserve parallel wrapper names such as openDelta, siteName, or signValue.

Semantic Object Name Parity

Shared semantic/runtime objects keep the same PascalCase type name in Lean and Rust. Field names may differ only by language casing convention: Lean uses camelCase, Rust uses snake_case. Proof-only Lean packaging objects and Rust-only operational wrappers are listed separately and are not parity targets.

Lean source of truth: lean/Runtime/ProtocolMachine/Model/SemanticObjects/Core.lean

Rust source of truth: rust/machine/src/semantic_objects.rs

Shared objectLeanRustStatus
Operation instanceOperationInstanceOperationInstanceExact name match
Outstanding effectOutstandingEffectOutstandingEffectExact name match
Semantic handoffSemanticHandoffSemanticHandoffExact name match
Transformation obligationTransformationObligationTransformationObligationExact name match
Authoritative readAuthoritativeReadAuthoritativeReadExact name match
Observed readObservedReadObservedReadExact name match
Materialization proofMaterializationProofMaterializationProofExact name match
Canonical handleCanonicalHandleCanonicalHandleExact name match
Publication eventPublicationEventPublicationEventExact name match
Progress contractProgressContractProgressContractExact name match
Progress transitionProgressTransitionProgressTransitionExact name match
Semantic object bundleProtocolMachineSemanticObjectsProtocolMachineSemanticObjectsExact name match
Shared enumLeanRustStatus
Operation phaseOperationPhaseOperationPhaseExact name match
Outstanding effect statusOutstandingEffectStatusOutstandingEffectStatusExact name match
Authoritative read kindAuthoritativeReadKindAuthoritativeReadKindExact name match
Authoritative read lifecycleAuthoritativeReadLifecycleAuthoritativeReadLifecycleExact name match
Canonical handle kindCanonicalHandleKindCanonicalHandleKindExact name match
Publication observer classPublicationObserverClassPublicationObserverClassExact name match
Publication statusPublicationStatusPublicationStatusExact name match
Progress stateProgressStateProgressStateExact name match
Ownership scopeOwnershipScopeOwnershipScopeExact name match
Delegation statusDelegationStatusDelegationStatusExact name match

Representative field casing mappings:

Lean fieldRust field
operationIdoperation_id
ownerIdowner_id
effectIdseffect_ids
terminalPublicationterminal_publication
publicationIdpublication_id
observerClassobserver_class
lastProgressTicklast_progress_tick

Lean-Only Proof Packaging Names

These are canonical Lean proof/runtime packaging objects not expected to have direct Rust runtime peers.

ProtocolMachineInvariantSpace, ProtocolMachineTheoremPack, ProtocolMachineRuntimeContracts, ProtocolMachineLivenessBundle, ProtocolMachineSchedulerBundle, ProtocolMachineEnvelopeAdherenceProtocol, ProtocolMachineEnvelopeAdmissionProtocol, ProtocolMachineBridgePremises, CoroutineViewEquiv, protocolMachinePotential.

Rust-Only Operational Wrapper Names

These are canonical Rust operational or embedding surfaces not expected to have direct Lean theorem peers.

GuestRuntime, ThreadedGuestRuntime, EffectHandler, ProtocolMachine, ProtocolMachineConfig, ProtocolMachineState, ProtocolMachineError, ProtocolMachineRunner, ProtocolMachineRunInput, ProtocolMachineRunOutput, ProtocolMachineReplayBundle, GuestRuntimeDeclaration.

Native worker-pool settlement deviation

The Rust-only private worker-pool disposal API concerns real host thread custody, not a Lean instruction, session status, capability witness or machine-tick transition. Its opaque receipt is issued from actual native JoinHandle results. It cannot reconstruct protocol authority or certify signed-terminal success.

Native join-operation unwind is distinct from a worker panic returned by a completed join. The original native custodian retains untouched handles, completed-join counts and chronological causes across that unwind. A consumed handle with unproven settlement permanently prevents whole-pool acknowledgment, even after same-original retry joins every remaining worker. This repairs host custody rather than adding a Lean instruction or protocol authority witness. Injected join-boundary tests establish this retention transaction, not an actual OS deadlock diagnosis or physical settlement of an uncertain consumed handle.

IdOwnerStatusReasonImpactAlternatives consideredRevisit dateCoverage scope
rust-host-native-pool-settlementtelltale-machine runtime maintainersActive, runtime-onlyNative worker exit and join are outside Lean protocol small-step stateConsuming explicit machine teardown joins original workers; partial construction retains actual worker handles and Rayon source; no instruction, wire or successful trace changeRayon Drop and targeted session receipt do not acknowledge private native-pool termination2027-01-20Eleven native source-declared component fixtures cover real worker TLS exit before ACK, original panic payload, actual machine pool, first spawn refusal and partial start followed by original joins, actual self-worker refusal returning the machine for external retry, join-operation unwind retaining untouched original workers and chronological causes, retained consumed-worker refusal after unwind, and observation during actual pending join; execution evidence remains required

Authority Binding Artifact Boundary

The communication-only theory artifact route cannot certify execution of an AST authority binding. It now rejects authority/observation modes, explicit check/observe/transfer expressions, linear aliases and unclassified calls rather than erasing those operations. Plain non-linear identifiers and constructors remain local communication abstractions. Their projection is not a value interpreter or authority witness.

The existing authority semantic-object conservation and supported-slice Lean theorems remain unchanged. They do not prove that the DSL compiler emitted an Invoke, passed the original nominal effect request, preserved typed outcome branching or bound a continuation to the issued owner. Those compiler and execution correspondence obligations remain open. A positive communication machine run and an explicit authority-artifact refusal are separate evidence.

In the current producer, a named Invoke action is recorded as action metadata, but step_invoke still submits the generic invokeStep request with role and register state. Renaming that action to a declared effect operation would not invoke that operation. Lean’s corresponding invokeStep request has the same role/state boundary. A nominal request/typed-outcome and continuation contract must be established at this shared boundary before claiming positive lowering.

Typed Outcome Acceptance Prerequisite

The Rust outcome adapters now separate raw status from accepted success. The same exact request/response-kind rule controls register updates, lifecycle publication and effect-composition success on cooperative and threaded paths. Recording and replay state adapters consume their actual returned outcome. They do not recover a state payload by rereading the latest shared trace entry.

The pinned release19 Lean model has eight request/response families, while Rust also has WalSync as a ninth family. The new pure acceptedStatusFor and invokeStepState helpers cover the actual eight modeled families. Their examples are not an execution correspondence proof. Existing EffectAlgebra.succeeded still classifies raw status, and ExecGuardEffect.stepInvoke executes the abstract total EffectRuntime.exec without a typed outcome gate. Connecting those premises to accepted typed outcomes, updating composition proofs and modeling WalSync are open correspondence prerequisites. The earlier durability alignment description must not be read as proof that the pinned request enum already contains WalSync.

Full nominal dispatch also remains open: the original declaration and uses admission, argument/result types, actual coroutine role and owner generation, result destination and success/failure continuation must be bound to one issued invocation. Late or duplicate completion must target that same live occurrence. No named audit string, post-hoc owner lookup or new side ledger establishes this contract. Host physical future and worker custody remain the embedding owner’s separate responsibility.

VM Invoke and Receive requests retain their actual configured register width as optional instruction context. All VM state-replacing responses, including branch Choose/Receive, must preserve that width before register replacement or effect- success publication. This enforces the canonical Rust wf_coroutine_state register-width invariant, not an inferred equal-length convention. Receive also retains its actual destination and requires that register to exist. Branch receive omits the destination because it performs no destination write, but retains width. After existing signature, replay and payload validation, both VM receive backends reject an out-of-range instruction destination with OutOfRegisters before invoking the host receive handler. This preserves the VM fault taxonomy and prevents host receive mutation for that invalid destination. It does not roll back earlier dequeue or replay work. Handler-only wrappers omit both instruction contexts. Missing fields on older serialized requests decode as absent and do not invent historical VM context. The original raw outcome remains observational evidence. This check is not rollback of earlier buffer dequeue, replay consumption, clock or type work. Optional observability retention cannot replace an original host failure: the canonical recorded exchange supplies lifecycle status without a second synthetic issue/complete cycle. Blocked remains a Blocked observation while the synchronous instruction reports its existing blocked-contract fault. The Lean request schema does not yet carry instruction width/destination context. Its pure family/status examples do not prove this Rust applicability rule. That schema and executable-semantics correspondence remain an explicit prerequisite.

Host-response acceptance is distinct from whole-instruction commit. In particular, Invoke may apply its accepted register state and host-success observation before persistence-delta application faults. No rollback of that state, emitted events, or earlier communication work is implied by the response-kind/destination guard. The prior cooperative owner could evict live enforcement inputs under diagnostic retention, while the prior threaded owner used untrimmed vectors. The canonical live/resident retention rule below repairs that storage boundary in both existing owners. Response/error preservation alone was insufficient. Neither correction claims nominal authority, a disposal acknowledgment or a Lean correspondence proof.

Serialized request context is an observation used to validate response shape, not proof of original request ownership or nominal authority. The replay handler selects by expected entry kind. It does not establish full incoming request identity. Actual VM producers attach current configured width before execution, including when replay supplies the response. Lean’s abstract effect-context step and spawn register copying are not a proof of this concrete Rust register ABI.

Canonical host exchanges also supply Acquire/Release/SendDecision outcome observations directly. Duplicate synthetic issue/complete cycles must not replace the original typed failure under optional diagnostic retention. Existing policy is preserved: cooperative Acquire Blocked waits on AcquireDenied, threaded Acquire Blocked reports its synchronous blocked-contract fault, and Release/SendDecision Blocked remain synchronous faults. The separate Send buffer WouldBlock producer retains its actual live capacity obligation: successful host decision is not queue admission, so that issue/complete pair is not a duplicate host outcome.

Canonical Live Effect Retention

Cooperative and threaded owners retain active effect, operation and progress records independently of Disabled/Capped diagnostic history. Both use the same live/resident classification and retired terminal-history pruning rule. Actual blocked Acquire and next-round topology producers must retain the original owner/budget and progress/reentrancy enforcement. Targeted disposal permits terminal history to obey its configured bound. The pre-existing instruction policy for Blocked Acquire still differs (cooperative blocks, threaded faults). Paired retention fixtures do not claim that wider behavior is equivalent.

Lean progress/reentrancy predicates concern retained semantic objects and their actual steps. They do not authorize observational eviction of an uncompleted operation. This Rust retention repair does not add a Rust/Lean correspondence proof, capture arbitrary asynchronous work, or equate invalidation with physical teardown. New fixture declarations require actual discovery and execution.

The original terminal-only retention child was insufficient: actual current_wal_sync_request consumes terminal operation/publication records through semantic_objects. Both backends now additionally protect every canonical record of an actually resident session. Terminal history eligibility starts only after actual session retirement. A terminal lifecycle status is not proof of nonresidency or irrelevance. A shared real Invoke/commit/WalSync fixture compares the exact handler request, agreement state and evidence under all three modes. Full correspondence and physical teardown remain separate obligations.

The composed required native ownership/disposal inventory contains 59 unique fixtures: the original 35 plus 17 strict-outcome/error, six live/resident retention and one cooperative full-queue/Disabled regression. The existing canonical checker requires their nonignored declarations, actual harness discovery and successful execution. Declarations alone are not passing evidence. Module filters can also execute required fixtures appearing in a narrower sub-suite, without changing the unique required count. Authority guard DSL/compiler/bridge fixtures and formal examples remain separate targets and obligations.

Explicit Placement Source Representation

The optional Protocol::Let.execution_role field preserves an explicit at Role clause through parsing, clone and continuation append. AST Protocol is not a serde wire type: no CodeImage or effect trace schema changes here. Existing Rust literal constructors now explicitly supply None. This is a Rust source migration, not an implicit role reconstruction. Legacy DSL forms remain unplaced.

Lean LocalTypeR contains communication and recursion only. CompileLocalTypeR cannot preserve a local typed authority binding. EffectRuntime.exec remains a total action/context transition. StepInvoke checks handler presence but has no nominal operation/generation/result-continuation transaction. Therefore no new Lean constructor, execution theorem or parity claim follows from source placement. The placed artifact refusal is deliberate until the actual shared model and Rust executable producers implement that transaction. Direct local projection of unplaced local expressions remains communication analysis, not proof that those expressions execute. This child does not change that existing analysis contract or classify its result as executable authority lowering.

Nominal Command Transaction Prerequisite

The Rust selected-operation compiler produces a static nominal declaration and call plus existing communication continuations. The owned loader retains the issued OwnedSession in coroutine custody. Serde deliberately omits that handle and the live result continuation. Static descriptors, effect traces and restored ownership metadata do not reconstruct it. A source-selected agreement profile is explicit on the existing OperationInstance. Legacy instances deserialize to None and retain their previous derivation rules.

Both Rust backends issue the canonical outstanding effect, operation and progress owner before the callback. The threaded path performs issuance before worker dispatch, retains the actual continuation on the original coroutine, and gates the main-thread result commit after the worker returns. The raw exchange remains separate from accepted status. Immediate Blocked is refused. MayBlock retains the selected original budget. Admission and commit use the inclusive original issued tick plus budget. Progress escalation never extends that endpoint. Semantic handler identity is observed before admission and once at final commit, with no subsequent identity callback after derived success publication. It is not proof of a physical provider identity or native-future settlement.

The executable Lean path uses typed action classifiers and invokeCommand, with the original typed action supplied to sum composition. The default callback refuses. Parsing/classification alone never falls through to generic exec. Products retain nominal classification but have no admitted result-combination contract, so their default nominal callback/relocation refuses. Legacy models with no nominal classifiers keep the existing generic path.

The model’s canonical coroutine, outstanding-effect, operation and progress fields retain the same nominal occurrence through Blocked and output refusal. The original typed exchange is an observation. A returned value is held before post-callback validation. Output refusal retains that holder instead of rolling back to the pre-issuance state. Exact logical-window, ordering preservation and typed-branch commit theorem declarations accompany real loader/opcode/commit fixtures. These source declarations require actual Lean checking. They are not a completed Rust/compiler refinement theorem.

Canonical Halt keeps the program counter at the actual terminal instruction while marking the coroutine done and removing its active endpoint type. Both Rust backends and the executable Lean transition use this same terminal PC. The pending nominal bridge case runs through declaration, the peer’s blocked choice, and the first invocation at logical tick three. It checks that actual original occurrence and its selected budget before testing retained authority. This does not change the scheduler clock or extend the original window.

Role entry PCs in Lean need not be zero. Static declarations apply only to the prefix of the actual role’s executable chunk, and the loader fixes that role on the original coroutine. A declaration is a static manifest, not an executed capability grant. Typed result branches must stay in that chunk. Converting an executable image to a role map now slices and rebases its actual instructions. It never recompiles local communication types and discards nominal actions. Typed action relocation must preserve the exact rebased descriptor or refuse. Missing/lying adapters and cross-role targets have negative fixture declarations.

Rust’s issued OwnedSession and generation checks remain the additional host boundary described by rust-host-owned-session-parity. Lean endpoint/occurrence records do not reconstruct that handle or prove physical host cleanup. The source compiler, both backend fixtures and executable Lean relation are still uncompiled/unexecuted in this prepared child. Full source-to-model refinement, authoritative evidence/readiness, publication, materialization and transfer lowering remain required and open. No broader authority proof follows from this Command application-result slice.

Executable nominal bridge scope

The existing protocol-machine runner has a separate runNominalImage operation. It installs the source compiler’s actual tagged instructions through the checked role-image loader. Missing bytecode or typed outcomes cannot fall back to local communication-type recompilation. The legacy communication operation remains communication-only. The targeted Lean prerequisite builds this runner module and its nominal model fixtures explicitly before lean --run imports them.

The comparison retains actual application outcomes, accepted statuses, semantic fault payloads, issuance and callback-observation ticks, operation/profile/proof requirements, registers, PCs, and readonly observations of the original pending continuation, staged result and independent refusal. These observations contain no OwnedSession and cannot restore authority. Unsupported typed responses and terminal fault families refuse rather than becoming absent payloads.

The source-derived normalization ledger permits only the validated single original occurrence-ID relation and an exact original-or-plus-one reply relation. It does not equate arbitrary foreign IDs, normalize ticks, or erase retained results. Explicit local payload annotations and unsupported instructions remain outside this narrow image bridge and refuse. The fixture declarations include actual Blocked and output-denied executions plus omitted-holder, changed-result, changed-terminal-cause and foreign-ID controls. They require real Rust/Lean execution. No source-only preparation counts as correspondence acceptance or a full compiler-refinement theorem.

Finite simulation-counter admission

Lean Runtime/ProtocolMachine/Runtime/Runner.lean::runScheduled and ThreadedRunner.lean::runScheduledThreaded advance the model clock by one before the round. Rust preserves that one-round increment over its representable domain. Its u64 tick and Duration are finite, unlike the mathematical model clock. At either boundary, Rust returns a typed admission error before changing either counter or entering the round. This refuses an unrepresentable execution. It never saturates, wraps, advances per worker wave, or substitutes a physical clock.

IdOwnerStatusReasonImpactAlternatives consideredRevisit dateCoverage scope
rust-finite-simclock-admissiontelltale-machine runtime maintainersActive, finite representation boundaryRust tick and simulated duration have finite successors while Lean model arithmetic is unboundedTyped atomic refusal before round mutation; all representable rounds retain one tick each; no authority or cleanup ACKWrapping, saturation, panic or partial tick mutation changes the selected model time2027-01-21Three direct clock declarations and two actual-round declarations per backend; source-only until discovered and executed

Finite effect-occurrence admission

Rust’s canonical effect-occurrence counter is finite. Its exhausted sentinel is u64::MAX. An occurrence is allocated only below that value. Lean’s natural number counter has no corresponding finite boundary. Correspondence requires capacity for the complete selected dispatch or dynamic topology batch. This premise is separate from simulation-clock admission and never renews an original operation’s tick, deadline, owner or progress window.

Both Rust backends check a conservative fresh-occurrence bound before instruction or host mutation. Threaded dispatch checks the sum before its first nominal issuance or worker. The check holds capacity without advancing the counter. Unused capacity creates no holes and ordinary occurrence order stays unchanged. A topology poll needs its own slot. Its actual outcome is retained before a second check covers every returned event, before the first topology mutation. Conservative bounds may refuse a smaller outcome that would have fit alone. Refusal is typed and does not acknowledge worker cleanup. Independent timeout witness capacity must also fit before a topology batch applies. Unlike atomic SimClock admission, effect-capacity refusal may follow the admitted round’s tick increment and scheduler selection. A public round at u64::MAX refuses before its topology poll. Entering at u64::MAX - 1 can admit that poll and leave the instruction boundary at exhaustion for zero-fresh original settlement.

An already issued nominal callback can return Failure or Blocked with no fresh occurrence. It keeps its original invocation and selected window at exhaustion. Success and application Err retain their actual typed result before publication capacity is checked. If publication cannot fit, existing completion settles the same original canonical effect as Failed, including an unparented invocation, and updates its operation/progress state. The actual callback outcome remains unchanged and its typed returned result and independent refusal remain resident. No hint, WAL effect, readiness consumption or successful continuation is authorized. This is terminal refusal, not positive retry. A fresh nominal invocation must fit its complete bound before issuance. For a threaded fallback only the actual first, already issued nominal pick may run. Other original picks return to the existing scheduler.

IdOwnerStatusReasonImpactAlternatives consideredRevisit dateCoverage scope
rust-finite-effect-occurrence-admissiontelltale-machine runtime maintainersPrepared finite representation boundaryRust canonical occurrence IDs are finite while Lean model IDs are natural numbersComplete-dispatch and topology-batch capacity premise, preserving zero-fresh original settlementSaturation, wrapping, eager unused IDs, per-worker allocation, or replacing the original occurrence2027-01-21Source preparation, backend/nominal/topology/wave fixtures require actual discovery and execution

Prepared readiness continuation and session authority state

The typed nominal result now distinguishes Boolean application values from ReadinessWitness data. Both Rust backends use the existing canonical session issuer/consumer and the retained original OwnedSession. The witness is consumed only at gated commit. Lean adds owner/current-generation and issued/consumed readiness state to the same canonical SessionState, with explicit model owner admission and issuance independent of callback fields. The original model owner is retained in the nominal continuation. Model data is not an issued Rust host handle, and does not establish physical resource custody or asynchronous cleanup.

Value and ValType readiness cases are appended, preserving prior enum order. Witness data keeps the original registry witness field order and scope shape. The previously unreleased nominal reply uses an explicitly tagged typed result (the Bool-only nominal draft shape is rejected). This is not claimed compatible with old draft nominal JSON/binary artifacts. Released v19 has no nominal reply surface. No canonical replay/trace v1 field or existing variant is reordered. Original owner/continuation serde skips are unchanged: decoding a register or witness never restores live authority. Both JSON and canonical binary roundtrips and old value/type tags have declared regressions. Broad source-to-model bridge normalization must explicitly handle the new typed result and cannot erase it.

New Rust and Lean cases are uncompiled/unexecuted source. No static proof of physical custody, complete DSL authority lowering, or restoration follows.

The prepared existing compiled-image bridge uses nominal_image.v2 for explicit typed Boolean/readiness results and complete original owner/registry observations. Admission input carries only an owner intent and an after-Blocked scheduling choice. The actual original runtime/model session issues the witness between rounds after a genuine Blocked occurrence. Transported witness fields cannot mint authority. Commit uses the canonical store-owned audit adapter with the current SimClock tick. Observations include genuine issue, consumption and rejection. The single-original effect-id relation does not normalize witness ids, owner, session, generation, predicate, callback ticks, ordering or completion ticks.

The canonical OutstandingEffect.applyEvent contract preserves the original ordering key for blocked/completed/timeout/cancellation/invalidation transitions. Only an explicit retry event replaces it. The prepared native producers now preserve that key, retain no terminal completion for a Blocked transition, and record the actual current tick for terminal settlement. Bridge output separates original request issuance, ordering key and completion tick. The existing model ordering-preservation theorem remains unchanged. This source correction and the actual Blocked-to-later-success regressions still require execution.

Prepared original source-operation correspondence

The existing compiled-image bridge now preserves typed Begin/Await/Resolve/ Invalidate opcodes and exact required-child manifests. It does not regenerate these actions from communication projections. Both native backends retain the same enclosing coroutine holder, distinct from the singleton effect:id child, and the executable model uses the same canonical coroutine, session ownership, operation, effect and progress fields. Every selected nominal call captures the original coroutine-held model owner through the canonical owning-load admission. Looking up the current registry owner is insufficient. Model owner data does not prove Rust opaque-handle issuance or native resource custody.

Comparison includes the original enclosing declaration, admitted slots, actual child outcome, proposed resolution, typed first execution failure and canonical parent/progress records. The raw original parent id is retained. Its comparison relation validates the actual role entry and installed original PC before relating the role-local PC and derived occurrence id. It does not normalize ticks, generations, policy, values or failures. Child ids follow the existing validated single-original relation. Missing parent records or executable payload, unknown response families and unsupported fault observations refuse comparison. This is a prepared executable relation, not an elaborated compiler-refinement theorem or full authority-language proof. Required Rust/Lean execution is open.

Finite session-identifier representation

Lean’s session ids are natural numbers. WFVMState requires every resident sid to be below nextSessionId. Source Open advances that natural-number counter, and the public loader’s nextFreshSessionId avoids resident ids even for noncanonical input. Rust’s shared checked session-open admission retains this freshness within its finite representable range and explicitly refuses exhausted or non-fresh ids. It never wraps, saturates into reuse, replaces a resident, or reconstructs an owner. The new typed Rust allocation fault has no corresponding exhausted-Nat Lean case. Parity therefore requires the finite fresh-allocation premise. It does not assert that Lean’s unbounded execution takes a finite host refusal step.

The existing finite-effect occurrence admission remains a separate counter family. Session allocation uses the original session-store high-water mark and exclusive store transaction, not an effect occurrence, extra allocator, nonce registry or new authority. Source and backend boundary tests in this preparation still require actual harness discovery and execution, native/default/WASM configuration checks, strict lint and the required full final-source model/CI/release gates.

Host Lean child custody

Lean bridge process deadlines are physical host execution bounds, outside the simulated protocol-machine clock. The original admitted monotonic horizon also covers source-fallback configuration selection and input transmission. Neither cleanup grace nor model ticks restart or widen that operation horizon.

The bridge retains its original Unix child group, stdin writer and separate stdout/stderr readers until physical settlement. Source-backed runner and validator commands delegate through the canonical project launcher, which transfers its actual local writer-lock descriptor into the selected shared or native owner. Shared protocol-v1 custody retains that descriptor through its original Lake lock, budget lease and timing command. It is not reconstructed from a PID, an observation, or a serialized record. On timeout the bridge asks the original owner to cancel and waits. It never hardkills the sole shared cleanup owner first. The shared owner retains its own existing escalation and actual child-wait responsibility. Native/custom group cleanup has its separate existing two-second TERM grace and actual wait, without claiming custody of arbitrarily detached descendants.

Primary I/O failures, independent cleanup causes, original child status and partial separate streams remain retained. SIGKILL cannot be trapped. An external hardkill or a lost native wait is not evidence of successful descendant cleanup. This host contract supplies no theorem-pack inventory, semantic publication, cryptographic authority or model-time transition. Positive execution and same-binary negative controls on the final release source remain required.

Version 22 is a breaking bridge API transition: the public exhaustive LeanRunnerError, ProtocolMachineRunnerError, and HeapParityRunnerError variants retain cleanup causes, original child status, and partial streams. Downstream timeout patterns must bind those fields or use ... Constructing these variants requires the new fields. Original I/O errors remain available through the error source. The prelude continues to reexport the same types. Aura’s current Quint adapter only reads LEAN_BRIDGE_SCHEMA_VERSION and needs no error-pattern migration. This API change does not manufacture Lean evidence.

The explicitly selected protocol2 host launcher uses the existing shared resource request while retaining custody protocol1. The original one-worker profile reserves8168MiB, including the original1024MiB overhead and7144MiB safe worker reservation. This complete charge is independent of the shared ledger’s5120MiB compatibility weight. The protocol2 adapter refuses a different worker set. Ordinary protocol1 calls retain their original worker arguments and caller overrides. It retains the actual unlinked request descriptor and original writer descriptor through the existing pass-fd chain. Resource names identify canonical scheduling domains and do not reconstruct live authority. Source identity, when provided, is the existing outer admission fingerprint and remains observation metadata.

A shared refusal never retries native execution. A missing protocol2 endpoint permits the announced local path only when the original one-worker profile fits the selected capacity and reserve. This is no machine-wide grant or physical teardown proof. Protocol-v2 forwarded cancellation returns the original wrapper classification143. The canonical timing record separately retains the actual inner command outcome, including a trap’s native41. Consumers retain both outcomes and actual cleanup acknowledgments. Neither invalidation nor an equal-valued identifier proves that the original child, writer descriptor or memory lease has settled.

Theorem Program

This document maps the three-paper theorem program to implemented Lean modules and runtime surfaces.

3 Paper Structure

The theorem program is organized into three stages.

StagePaper focusCore output
Paper 1coherence and effect bridgepreservation kernel and typed protocol-machine bridge premises
Paper 2quantitative and decision dynamicsbounds, decidability packages, crash and Byzantine interfaces
Paper 3reconfiguration and envelope adequacyharmony under reconfiguration, envelope exactness, admission and adherence

Paper 1 Mapping

Paper 1 centers on operational coherence and the Consume kernel.

Claim familyLean anchor modules
message-type alignment via ConsumeProtocol/Coherence/Consume.lean
subtype replacement and coherence liftProtocol/Coherence/SubtypeReplacement*.lean
coherence preservation stackProtocol/Preservation*.lean, Protocol/Coherence/*
typed effect bridge to protocol machineRuntime/Proofs/protocol machine/BridgeStrengthening.lean, Runtime/Proofs/EffectBisim/*

Claim scope is assumption-scoped. Delivery and monitor premises remain explicit in the bridge layer.

Paper 2 Mapping

Paper 2 adds quantitative dynamics and algorithmic decision surfaces.

Claim familyLean anchor modules
weighted quantitative descent and scheduler-lifted boundsRuntime/Proofs/WeightedMeasure/*, Runtime/Proofs/SchedulingBound*.lean, Runtime/Proofs/Lyapunov.lean
regular finite-reachability decidabilitySessionCoTypes/AsyncSubtyping/*, SessionCoTypes/Coinductive/Regular*.lean
crash-stop characterizationProtocol/CrashTolerance.lean
Byzantine exact safety interface packagedistributed adapter and theorem-pack modules

Bound classes differ by theorem. Productive-step bounds are exact under premises. Scheduler-lifted totals are profile-dependent conservative bounds.

Paper 3 Mapping

Paper 3 integrates reconfiguration, exact envelope characterization, and protocol-machine adherence.

Claim familyLean anchor modules
reconfiguration harmony for link and delegationProtocol/Deployment/Linking*.lean, harmony and delegation modules
relative minimality and composed-system conservationProtocol/Coherence/*, linking and Lyapunov modules
envelope exactness and normalizationRuntime/Adequacy/EnvelopeCore/*
adequacy and runtime adherenceRuntime/Adequacy/*, Runtime/Proofs/TheoremPack/*
Byzantine envelope extensiondistributed adapters and theorem-pack Byzantine surfaces

This stage ties proof objects to runtime capability and admission surfaces.

Assumption and Boundary Model

Each theorem family is tied to explicit assumption bundles.

Boundary classMeaning
exact under assumptionstheorem gives iff or maximality class under declared premises
conservative under profiletheorem gives safe upper bound or admitted envelope class
interface onlypackage provides typed boundary and witness hooks without stronger global claim
out of scopeintentionally deferred claim class

Scheduler-lifted total-step bounds are an example of the conservative class. Byzantine liveness outside declared timing and fairness bundles falls under out of scope.

The theorem program is operationalized through theorem-pack inventory and admission gates.

Runtime/Proofs/TheoremPack/API.lean provides gate aliases. Runtime/Proofs/TheoremPack/Inventory.lean provides capability key extraction. Runtime/Adequacy/EnvelopeCore/AdmissionLogic.lean provides admission soundness, completeness, and diagnostics categories.

Rust admission paths in rust/machine/src/runtime_contracts.rs and rust/machine/src/composition.rs consume these proof-facing concepts as executable gates.

Classical theorem packs follow the same path: protocol or transport constructors build semantic inputs, runtime adapter profiles attach them to the invariant space, and buildProtocolMachineTheoremPack emits artifacts and inventory keys. The current classical keys are documentation-background inventory, not Rust runtime-admission gates, and the real-analysis dependency is isolated behind the named ClassicalAnalysis API/Instance boundary. The classical families are honest conditional interfaces: Little’s law, MaxWeight optimality, concentration tails, and spectral-gap termination are assumed inputs, and the heavy-traffic, mean-field, and functional-CLT families prove only elementary identities. classicalEvidenceInventory records the class of each key.

Theorem Pack Inputs

This document catalogs every input that can contribute to a ProtocolMachineTheoremPack. The pack is the capability surface consumed by release gates, runtime admission paths, and conformance reports. All theorem-pack artifacts are derived from one ProtocolMachineInvariantSpaceWithProfiles by buildProtocolMachineTheoremPack in lean/Runtime/Proofs/TheoremPack/Build.lean.

Pack Shape

ProtocolMachineTheoremPack is a record of Option artifact fields, one per theorem family. Inputs arrive in two shapes. Invariant-space inputs attach via API setters on the combined space. Optional profile inputs attach via family-specific with* setters and are consumed by the builder.

The combined invariant space is ProtocolMachineInvariantSpaceWithProfiles in lean/Runtime/Proofs/TheoremPack/Profiles.lean. It extends the distributed-profile space and adds a classical profile record. Two projection views (toDistributedSpace, toClassicalSpace) let builders consume the distributed or classical subset in isolation.

Core Invariant-Space Inputs

Three input families enter the pack directly from the invariant space. They do not require a distributed or classical profile.

FamilySource fieldAPI setterInventory key
terminationliveness?withLivenessBundletermination
output condition soundnessoutputConditionWitness?withOutputConditionoutput_condition_soundness
semantic objectssemanticObjectWitnesses?withSemanticObjectWitnessessemantic_object_*

Termination artifacts use Adapters.toLivenessBundle? from lean/Runtime/Proofs/Adapters/Progress.lean and the theorem protocol_machine_termination_from_invariant_space. The termination bound is derived (FairTermination.fair_termination) from one-step ranking obligations in ProtocolMachineTerminationModel (measure non-increasing, strict decrease on enabled steps, enabledness stable under unproductive steps, non-complete ⇒ some role enabled). Those obligations are not yet discharged for the concrete execInstr step. Output-condition artifacts lift soundness from outputConditionWitness? into OutputConditionArtifact. Semantic-object artifacts are built by SemanticObjectArtifacts.ofWitnessBundle and cover core invariants, outstanding effects, semantic handoffs, authoritative-read publication, materialization success, progress contracts, effect contracts, replay failure exactness, cross-target progress-dependent work, and transformation-local obligations.

Distributed Families

Distributed profile wrappers are defined in lean/Runtime/Proofs/Adapters/Distributed/ProfileWrappers.lean. Each family attaches through a with* setter on the combined space. Inventory keys are string labels from lean/Runtime/Proofs/TheoremPack/Inventory.lean, not Lean theorem names. Each key reports whether the profile artifact is present. The theorems are carried by the family profile. For example, quorum_geometry_safety corresponds to Distributed.QuorumGeometry.no_conflicting_commits_of_protocol, and accountable_safety corresponds to Distributed.AccountableSafety.accountable_safety_of_protocol.

FamilyProfile setterInventory key
FLP lower boundwithFLPflp_lower_bound
FLP impossibilitywithFLPflp_impossibility
CAP impossibilitywithCAPcap_impossibility
quorum geometrywithQuorumGeometryquorum_geometry_safety
partial synchronywithPartialSynchronypartial_synchrony_liveness
responsivenesswithResponsivenessresponsiveness
Nakamoto securitywithNakamotonakamoto_security
reconfigurationwithReconfigurationreconfiguration_safety
atomic broadcastwithAtomicBroadcastatomic_broadcast_ordering
accountable safetywithAccountableSafetyaccountable_safety
failure detectorswithFailureDetectorsfailure_detector_boundaries
data availabilitywithDataAvailabilitydata_availability_retrievability
coordinationwithCoordinationcoordination_characterization
CRDT envelope familywithCRDTcrdt_envelope_and_equivalence
CRDT op-core erasurewithCRDTErasure(stored in pack as crdtErasure?)
triangle of forgettingwithTriangleOfForgettingtriangle_of_forgetting_impossibility
Byzantine safetywithByzantineSafetybyzantine_safety_characterization
consensus envelopewithConsensusEnvelopeconsensus_envelope
failure envelopewithFailureEnvelopefailure_envelope
protocol machine envelope adherencewithProtocolMachineEnvelopeAdherenceprotocol_machine_envelope_adherence
protocol machine envelope admissionwithProtocolMachineEnvelopeAdmissionprotocol_machine_envelope_admission
protocol envelope bridgewithProtocolEnvelopeBridgeprotocol_envelope_bridge

Three inventory keys are derived from other profiles and have no distinct setter. calm_characterization is derived from the coordination profile. crdt_monotonicity is derived from the CRDT profile. effect_interface_bridge is derived from the conjunction of protocolMachineEnvelopeAdherence? and protocolEnvelopeBridge?.

Classical Families

Classical wrappers are defined in lean/Runtime/Proofs/Adapters/Classical.lean. The inventory keys below are string labels, not Lean theorem names. Each classical family has a per-family setter on ProtocolMachineInvariantSpaceWithProfiles. withClassicalProfiles remains available as a batch replacement API when a caller wants to replace the whole ClassicalProfiles record.

FamilySetterWrapper typeInventory key
Foster-LyapunovwithFosterFosterProfileclassical_foster
MaxWeightwithMaxWeightMaxWeightProfileclassical_maxweight
large deviationswithLDPLDPProfileclassical_ldp
mean-fieldwithMeanFieldMeanFieldProfileclassical_mean_field
heavy-trafficwithHeavyTrafficHeavyTrafficProfileclassical_heavy_traffic
mixing timewithMixingMixingProfileclassical_mixing
fluid limitwithFluidFluidProfileclassical_fluid
concentration boundswithConcentrationConcentrationProfileclassical_concentration
Little’s lawwithLittlesLawLittlesLawProfileclassical_littles_law
functional CLTwithFunctionalCLTFunctionalCLTProfileclassical_functional_clt
spectral-gap terminationwithSpectralGapSpectralGapProfileclassical_spectral_gap_termination

Classical families are honest conditional interfaces (“Option B”). The named classical theorems (Little’s law, MaxWeight optimality, sub-Gaussian tails, large-deviation and mixing envelopes, spectral-gap termination, Cheeger) are not proved in Lean. They enter as caller-supplied witness fields, and an inventory flag of true only records that an artifact is present. classicalEvidenceInventory in Runtime.Proofs.TheoremPack.Inventory classifies each key:

Inventory keyEvidence class
classical_fosterprovedDeterministic (deterministic drift; no Harris recurrence)
classical_maxweightassumedClassicalInput
classical_ldpderivedFromAssumedInput
classical_mean_fieldelementaryIdentity (finite-average reindexing)
classical_heavy_trafficelementaryIdentity ((σ√n)² = σ²n)
classical_mixingderivedFromAssumedInput
classical_fluidderivedFromAssumedInput
classical_concentrationassumedClassicalInput (tail bound)
classical_littles_lawassumedClassicalInput
classical_functional_cltelementaryIdentity (degenerate constant path; not a CLT)
classical_spectral_gap_terminationassumedClassicalInput

Execution Profile

ProtocolMachineExecutionProfile is the proof-carrying execution context derived from the invariant space. It fixes fairness assumptions, admissibility classes, escalation-window classes, a theorem-pack eligibility list, and a necessity catalog. The builder function is ProtocolMachineInvariantSpaceWithProfiles.executionProfile.

Fairness assumptions enumerate scheduleConfluence, starvationFreedom, and livenessFairness. These are declared assumptions. The proved scheduler fact is schedule_nonblocking (one-step: a queued ready coroutine means schedule steps), which is not starvation freedom. The scheduler artifact’s scheduleDeterministic field records selection determinism. Confluence of independent steps is LocalStepSystem.independent_steps_commute and is not yet instantiated for execInstr. Admissibility classes cover localEnvelope, shardedEnvelope, and protocolEnvelopeBridge. Escalation-window classes cover progressContractBounded, admissionComplexityBounded, and protocolBridgeBounded.

The execution profile also carries a list of TransportNecessityProfile values. executionProfile_necessity_hardened_of_all_proven_necessary and executionProfile_necessity_minimal_basis_of_hardened_and_oracles lift per-profile necessity into catalog-level hardening and minimal-basis closure. Both theorems are stated in lean/Runtime/Proofs/TheoremPack/Profiles.lean.

Proof-Carrying Metadata

ProofCarryingArtifactMetadata summarizes profile, progress, and envelope metadata alongside each pack.

ComponentRecordSource
execution profile and eligibilityProfileArtifactMetadataexecution profile on the combined space
progress contracts and failure taxonomyProgressArtifactMetadatasemantic-object witnesses
envelope adherence, admission, bridgeEnvelopeArtifactMetadataderived distributed profiles

ProofCarryingArtifactMetadata.ofInvariantSpace assembles these from the combined space and the three envelope-presence booleans. Metadata inventory keys are enumerated in proofCarryingMetadataInventory in lean/Runtime/Proofs/TheoremPack/Inventory.lean. The metadata_* keys end in _premise_supplied and record presence only. metadata_progress_contracts_premise_supplied tracks semantic-object witnesses, while metadata_replay_failure_exactness_premise_supplied tracks the replay/failure-exactness witness specifically.

Inventory and Capability Keys

theoremInventory in lean/Runtime/Proofs/TheoremPack/Inventory.lean projects the pack into a flat list of (key, Bool) pairs. Keys cover termination, output-condition soundness, every distributed family, every classical family, the effect-interface bridge, and each semantic-object component. Release and admission paths consume this inventory rather than the pack directly. Each Bool is a presence flag meaning “premise bundle supplied”, not “proved”. Key strings are kept stable because Rust consumes them.

theoremInventoryWithDeterminism augments the inventory with determinism-policy booleans. The semantic-object sub-inventory is produced by SemanticObjectArtifacts.inventory and folds each semantic witness into a capability key under the semantic_object_* prefix.

Admission Boundary

transportedTheoremBoundaryInventory in lean/Runtime/Proofs/TheoremPack/AdmissionBoundary.lean tags each inventory key with a TransportedTheoremUsageClass. The class distinguishes black-box premise use, runtime-critical instantiated premises, and documentation-only references.

runtimeCriticalTransportedTheoremBoundaryInventory narrows the ledger to families that gate runtime admission. rustRuntimeCriticalTransportedTheoremKeys and leanRuntimeCriticalTransportedTheoremKeys expose the Rust and Lean subsets consumed by admission paths. runtimeCriticalTransportedTheoremsExplicit_true certifies that the Rust and Lean lists agree.

Lean Evidence for Envelope Keys

Rust admission of the three envelope keys (protocol_machine_envelope_adherence, protocol_machine_envelope_admission, protocol_envelope_bridge) is evidence-backed. ProtocolMachineExecutionProfile and TheoremPackCapabilities carry a LeanEvidenceInventory (key to Bool), parsed by LeanEvidenceInventory::from_json_str from the Lean theoremInventory export, either as a {key: bool} object or as the List (String × Bool) encoding [[key, bool], ...]. execution_profile_supported admits a required envelope key only when that inventory reports true. Self-asserted theorem_pack_eligibility Bools without evidence are rejected. Missing keys are not admitted.

The defaults (ProtocolMachineExecutionProfile::full(), TheoremPackCapabilities::full()) carry an empty inventory, so they do not admit any envelope key. Callers attach evidence with with_lean_evidence. The explicit test and development opt-in is assume_envelope_evidence_for_testing() on either type. It asserts all three keys without Lean evidence and must not be used in production admission paths. An inventory flag records that a premise bundle was supplied, not that the premise was proved.

Transport Contracts

Theorem-backed distributed claims also require selected transport contracts. The Rust machine consumes RuntimeTransportContract values as semantic evidence, not as TCP-, TLS-, or key-management-specific implementation details. Concrete transport crates are responsible for mapping their configured mode into those fields.

The standard protocol-origin profile requires role-addressed routing, authenticated peers, per-peer FIFO delivery, fail-closed unknown-role handling, and no message synthesis. For telltale-transport, TcpTransportConfig::runtime_transport_contract() maps pre-shared-key TCP to authenticated_peers = true and trusted-network TCP to authenticated_peers = false. Trusted-network contracts are intentionally rejected for theorem-pack profiles that depend on authenticated protocol origins.

Release Conformance

buildReleaseConformanceReport in lean/Runtime/Proofs/TheoremPack/ReleaseConformance.lean assembles a ReleaseConformanceReport from the pack, a ReleaseRunEvidence record, and a replay trace. Each gate checks distinct evidence. The cross-target and restart gates require the failure-envelope premise bundle plus the crossTargetRunsAgree and restartReplayPassed run bits respectively. The single-thread gate requires the adherence premise bundle plus singleThreadRunPassed. The multi-thread gate requires multiThreadRunPassed. The sharded gate requires the protocol-bridge premise bundle plus shardedRunPassed. releaseReady is always the conjunction of replay conformance and all five gates. There is no untagged bypass, and ReleaseRunEvidence.none fails every gate. releaseBuildGate_sound proves that a passing gate implies each piece of evidence. Gates report that premises were supplied and runs passed, not that the premises were proved.

Optimization transformations are enumerated by RuntimeTransformationEnvelopeClass. The four classes are schedulerPermutation, waveWidening, effectReordering, and failureReplayNormalization. transformationClassRequiredCapabilities defines the inventory keys each class requires, and transformationClassEligible is the inventory gate.

CertifiedReplayEquivalenceClass pairs a transformation class with a certificate reference. defaultCertifiedReplayClasses is the canonical list admitted by release gates. replayConformsToClasses is the per-class replay gate used by the build gate.

Capability API Gates

lean/Runtime/Proofs/TheoremPack/API.lean exposes the runtime capability gates that consume the pack.

GateArtifact requirement
canAdmitShardPlacementprotocolEnvelopeBridge?
canLiveMigrateprotocolEnvelopeBridge?
canRefinePlacementprotocolEnvelopeBridge?
canRelaxReorderingprotocolEnvelopeBridge?
canUseMixedDeterminismProfilesprotocolMachineEnvelopeAdherence? and protocolMachineEnvelopeAdmission?
canOperateUnderByzantineEnvelopebyzantineSafety? and protocolMachineEnvelopeAdherence?
canAutoscaleOrRepartitionprotocolEnvelopeBridge?

Rust admission paths in rust/machine/src/runtime_contracts.rs and rust/machine/src/composition.rs consume these gates as executable admission checks. claimedCapabilitiesWithinInventory verifies that a list of declared capability tags is supported by the pack capabilities. envelopeCapabilitySnapshot extracts the envelope subset used by conformance reports.

Assumption Discipline

Each family carries a proof-bearing semantic profile. Completed families avoid final-theorem witness fields. Profiles expose smaller laws such as delivery and ordering facts, quorum geometry, timed-run predicates, CRDT laws, chain growth and quality, shard reconstruction, or slashable-evidence construction.

Capability bits indicate that the theorem-pack builder derived the corresponding artifact from a profile. They do not imply stronger claims outside the family assumption bundle or an explicit documented trust boundary.

Runtime Admission Impact

Runtime features that require profile evidence are gate-controlled. Examples include mixed determinism profiles, Byzantine envelope operation, autoscaling and repartition requests, and placement refinement. Gate aliases are provided in lean/Runtime/Proofs/TheoremPack/API.lean. Rust runtime admission paths consume these aliases.

API Reference

This document provides a high level map of the public APIs. For full signatures, use the crate level lib.rs files and generated rustdoc.

Core Crates

telltale

Core session type library with channel primitives and macros.

Key exports:

  • GlobalType, LocalTypeR, Label, PayloadSort
  • Role, Roles, Message derive macros
  • Channel traits and session state types

See rust/src/lib.rs for the full list of re-exports.

Generated tell! APIs use one canonical effect-boundary import style:

  • import Protocol::effects
  • effects::Runtime for host traits
  • effects::RuntimeRequest / effects::RuntimeOutcome for typed dispatch
  • effects::runtime::operation("ready") or effects::runtime::READY for generated per-operation semantic metadata
  • Protocol::proof_status for theorem-pack, tier, parity, and agreement/finality metadata

telltale-types

Type definitions shared across the stack.

Key exports:

  • GlobalType, LocalTypeR, Label, PayloadSort
  • ContentId, Blake3Hasher, ContentStore, KeyedContentStore
  • DefaultContentHasher and DefaultContentId for central content-hash policy
  • Sha256Hasher and ContentIdSha256 when the sha256 feature is enabled
  • Merge helpers (merge, merge_all, can_merge) and canonical-serialization utilities

See rust/types/src/lib.rs for re-exports.

telltale-theory

Session-type algorithms and executable theory checks.

Key modules:

  • Projection: telltale_theory::projection::{project, project_all, MemoizedProjector}
  • Merge, duality, well-formedness, and semantics checks
  • Subtyping (feature-gated): telltale_theory::subtyping::{async_subtype, sync_subtype}

Exports are module-scoped, not re-exported at crate root. See rust/theory/src/lib.rs for the complete feature-gated API.

telltale-runtime

Choreographic DSL, projection, and effect execution.

Key exports:

  • AST types: Choreography, Protocol, Role, MessageType
  • Effect system: Program, ProgramBuilder, interpret
  • Handlers: ChoreoHandler, InMemoryHandler, TelltaleHandler
  • Topology: Topology, TopologyHandler, TransportType
  • Heap: Heap, DefaultHeapHasher, Hasher, Resource, ResourceId, CanonicalHeapEncoding, CanonicalHeapEncoder, HEAP_ENCODING_MAGIC, HEAP_ENCODING_VERSION, resource_leaf_hash, nullifier_leaf_hash, merkle_node_hash, MerkleTree, HeapCommitment
  • Heap preimage helpers (module-scoped, not at crate root): telltale_runtime::heap::{resource_id_preimage, resource_leaf_preimage, nullifier_leaf_preimage, merkle_node_preimage, heap_commitment_preimage}
  • Extensions: ExtensionRegistry, GrammarExtension, ProtocolExtension

See rust/runtime/src/lib.rs for the full export surface.

telltale-machine

Protocol-machine and guest-runtime surfaces for executing projected local types.

Canonical public modules:

  • telltale_machine::model
  • telltale_machine::runtime
  • telltale_machine::semantics

Key exports:

  • ProtocolMachine, ProtocolMachineConfig, GuestRuntime, SchedPolicy, SimClock
  • Instr, Value, SessionStore, SessionId
  • OwnedSession, EffectHandler, and NestedProtocolMachineHandler
  • proof-aligned effect algebra: EffectSemanticClass, EffectRetryShape, EffectCompositionPolicy EffectResponsibilityDomain
  • canonical semantic objects: OperationInstance, OutstandingEffect, SemanticHandoff, TransformationObligation, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, PublicationEvent, Region, ProgressContract, ProgressTransition, ProtocolMachineSemanticObjects
  • first-class capability/finalization taxonomy: ProtocolCriticalCapabilityClass, ProtocolCriticalCapabilityLifecycleState, ProtocolCriticalCapabilityArtifact, ProtocolCriticalCapabilityLifecycleRecord, ProtocolMachineFinalization, FinalizationPath, FinalizationReadClass, FinalizationStage
  • proof-carrying runtime profiles: ProtocolMachineExecutionProfile, ProtocolMachineFairnessAssumption, ProtocolMachineAdmissibilityClass, ProtocolMachineEscalationWindowClass
  • runtime introspection (methods on ProtocolMachine / GuestRuntime): operation_instances(), outstanding_effects(), semantic_objects(), progress_contracts(), progress_transitions(), publication_events(), require_authoritative_read(), require_canonical_handle(), protocol_machine_finalization(), capability_lifecycle_audit_log(), semantic_audit_log(), canonical_replay_fragment()

GuestRuntime is the Telltale-owned runtime instantiated around the protocol machine. EffectHandler is the host-runtime boundary implemented by embedders and simulators.

telltale-search

Generic weighted-graph search substrate for the Telltale workspace.

Module structure:

  • telltale_search::cost
  • telltale_search::domain
  • telltale_search::machine
  • telltale_search::admission
  • telltale_search::runtime
  • telltale_search::observe

The supported boundary is intentionally generic:

  • canonical search-machine semantics
  • serial min-key batch extraction and deterministic commit
  • full legal batch execution even under chunked parallel schedulers
  • search replay and comparison artifacts
  • scheduler and fairness capability vocabulary
  • explicit graph-epoch and snapshot inputs

Downstream consumers are expected to provide application-specific node, edge, heuristic, and epoch semantics through typed domain traits.

Current serial-core exports include:

  • SearchCost, EpsilonMilli
  • SearchDomain, SearchQuery, SearchQueryError, SearchSelectedResultSemanticsClass
  • SearchMachine
  • CanonicalBatch, Proposal, ProposalKind, SelectedSolution
  • SearchBudgetState, SearchTraceState
  • SearchError, SearchInvariantViolation
  • profile/admission types: SearchDeterminismMode, SearchSchedulerProfile, SearchFairnessAssumption, SearchObservableClass, SearchClaimClass, CommutativityRegionClass, SearchDUser, SearchCertifiedCapability, AdmissionRejectionReason, check_capability_containment(...)
  • observation/comparison types: SearchObservationArtifact, NormalizedCommitRecord, SelectedSolutionPublicationRecord, ObservationComparison, ObservationRelation, compare_observations(...)
  • runtime/replay types: ProposalExecutor, SerialProposalExecutor, NativeParallelExecutor, NativeParallelExecutorError, AuthorityReadSet, AuthorityWriteSet, AuthoritySurface, SchedulerArtifact, SchedulerArtifactClass, ProgressSummary, TotalStepMode, SearchExecutionReport, SearchReplayArtifact, ReplayRoundRecord, ReplayExpectation, ReplayError, SearchRunConfig, SearchRunTermination, run_with_executor(...), replay_observation(...), EpochReconfigurationRequest, commit_epoch_reconfiguration(...), proposals_independent(...)

Replay validation is derived from canonical round commits rather than trusting stored summary fields when selected-result semantics are query-derived. For domain-defined selected-result semantics, replay fails closed instead of silently falling back to a compatibility goal anchor. Epoch reconfiguration is barriered and resets frontier/parent/incumbent state before reseeding the new epoch from the start node. Fairness bundles are explicit observation and admission surfaces. Observation artifacts also carry canonical parent identity and selected-result publication traces, while fairness and capability bundles use set semantics rather than order-sensitive vectors.

Import posture:

  • prefer SearchQuery::try_multi_goal(...) and SearchQuery::try_candidate_set(...)
  • prefer generic selected-result types over route/incumbent aliases
  • use telltale_search::compat only for legacy migration
  • treat SearchExecutionPolicy as an explicit compatibility matrix: CanonicalSerial => serial + width 1, ThreadedExactSingleLane => native parallel + width 1, BatchedParallelExact/BatchedParallelEnvelopeBounded => native parallel + width > 1
  • treat SchedulerStepBudget(n) as the supported budgeted-anytime effort mode and SearchCachingProfile::IncrementalReuse as outside the stable import posture until implemented

Optional layers above the core crate:

  • telltale_simulator::project_search_run(...)
  • telltale_viewer::project_search_artifacts(...)

Module access (not re-exported at crate root):

  • Effect boundary: telltale_machine::model::effects::EffectHandler, EffectRequest, EffectOutcome, EffectInterfaceMetadata, EffectExchangeRecord, EffectCompositionPolicy EffectSemanticClass, EffectRetryShape, EffectResponsibilityDomain, SendDecision, SendDecisionInput The typed internal durability request is EffectRequestBody::WalSync.
  • Effect trace: telltale_machine::model::effects::RecordingEffectHandler, ReplayEffectHandler
  • Durability: telltale_machine::model::durability::{AgreementWal, AgreementWalArtifact, AgreementWalEntry, AgreementWalHandler, EvidenceIdResolver, EvidenceOutcomeCache, EvidenceOutcomeCacheArtifact, EvidenceOutcomeCacheEntry, EvidencePersistenceHandler, DurableRecoveryAction, DurableRecoveryDecision, DurableRecoveryMetadata, DurableRecoveryPlan, FileAgreementWal, FileEvidenceOutcomeCache, InMemoryAgreementWal, InMemoryEvidenceOutcomeCache, PersistedDurabilityArtifact, PersistedDurabilityPayload, WalSyncMode, WalSyncRequest} These are the authoritative typed contracts for durable agreement WALs, evidence outcome caches, recovery metadata, typed recovery planning, and the internal wal_sync durability boundary. Helper/generated/viewer surfaces should consume projections of these artifacts rather than defining peer durable state. Downstream integrations should implement AgreementWal and/or EvidenceOutcomeCache directly rather than introducing backend-branded wrapper APIs. The supported contract is append/read/load plus fail-closed error returns. Storage-specific retries, replication, or transport details must remain behind that trait boundary.
  • Child-effect aggregation: EffectCompositionPolicy is a secondary sibling-effect algebra used beneath parent agreement contracts, not the top-level agreement model
  • Loader: telltale_machine::runtime::loader::CodeImage
  • Runtime contracts: telltale_machine::runtime::failure::{RuntimeContracts, ProtocolMachineExecutionProfile}
  • Runtime runner: telltale_machine::runtime::runner::{ProtocolMachine, GuestRuntime, StepResult, RunStatus}
  • Semantics: telltale_machine::semantics::exec::{ExecResult, ExecStatus, StepPack}

See rust/machine/src/lib.rs for the full API. See Effect Handlers and Session Types for integration-boundary guidance.

telltale-simulator

Simulation utilities built on the protocol machine.

Key exports:

  • Harness surface in rust/simulator/src/harness.rs: HostAdapter, DirectAdapter, FieldAdapter, HarnessSpec, HarnessConfig, SimulationHarness, BatchConfig, BatchRunResult FieldAdapter::from_scenario(...) requires built-in scenario field params. Generic harness runs may omit Scenario.field when the adapter supplies its own initial states or environment models. SimulationHarness::run_batch(...) and run_batch_with(...) preserve input-order results while parallelizing independent runs.

Module access (not re-exported at crate root):

  • telltale_simulator::trace::Trace, StepRecord
  • telltale_simulator::runner::run, run_concurrent, run_with_scenario, ChoreographySpec
  • telltale_simulator::scenario::{Scenario, ExecutionSpec, ExecutionBackend, ResolvedExecution, ResolvedExecutionBackend, DurabilitySpec, DurabilityMode}
  • telltale_simulator::runner::{resume_with_checkpoint_artifact, resume_with_durable_checkpoint_artifact, DurableResumeArtifacts, DurableResumeSummary} resume_with_checkpoint_artifact(...) is the exact non-durable checkpoint lane. Scenarios that declare DurabilityMode::Wal must resume through resume_with_durable_checkpoint_artifact(...) so the simulator cannot bypass the typed WAL/evidence contract.
  • telltale_simulator::durability::{FaultInjectingAgreementWal, DurableFaultProgram, ScheduledDurableFault, DurableFaultKind, DurableFaultRecord, DurableRecoveryRun, DurablePropertyReport, run_durable_recovery_case, durable_property_report} This is the reusable simulator-side durability assurance surface for deterministic fault injection, crash/recovery comparison, and durable property monitoring.
  • telltale_simulator::durability::{DurableInspectionReport, DurableWalEntryProjection, EvidenceCacheEntryProjection, inspect_durable_artifacts} These are observed-only tooling projections of authoritative durable artifacts for viewer and CLI inspection.
  • CLI surface: cargo run -p telltale-simulator --bin durable -- --wal <wal.cbor> --cache <cache.cbor> [--checkpoint <checkpoint.cbor> --scenario <scenario.toml>] [--json] This inspection-only lane projects typed WAL/cache/recovery artifacts without exposing backend-specific storage details.
  • telltale_simulator::generated::{GeneratedEffectScenario, GeneratedEffectScenarioBuilder, GeneratedEffectSimulationReport, ScenarioEffectDisposition, ScenarioEffectResult, ScenarioEffectStep} Helper-only generated-effect support. GeneratedEffectSimulationReport exposes helper accessors, not authoritative replay or theorem-classification fields.
  • telltale_simulator::{CheckpointArtifact, PersistedReplayArtifact, PersistedReplayPayload} Typed persisted replay/checkpoint wrappers for on-disk simulator artifacts. Durable agreement WALs and recovery metadata intentionally remain machine-level typed contracts instead of simulator helper exports.
  • telltale_viewer::{SemanticComparisonResult, TheoremAwareCounterexample, DeterministicSweepReport, ExperimentSuiteReport, EffectTraceArtifact, MinimizationResult, ViewerExtensionManifest} Shared viewer/webapp tooling surfaces for comparison, counterexamples, sweeps, effects, minimization, and downstream overlays.
  • Contract checks in rust/simulator/src/contracts.rs: ContractCheckConfig, ContractCheckReport, evaluate_contracts, assert_contracts
  • Preset helpers in rust/simulator/src/presets.rs
  • Field handlers and factory: IsingHandler, HamiltonianHandler, ContinuumFieldHandler, handler_from_field in rust/simulator/src/field_handlers/

telltale-bridge

Lean bridge for JSON export, import, and validation.

Key exports:

  • global_to_json, local_to_json, json_to_global, json_to_local
  • LeanRunner, Validator, ValidationResult
  • HeapParityRunner, HeapParityOutput
  • ProtocolMachineSemanticObjects and semantic-object schema helpers These come from the same canonical semantic-object family as telltale_machine::model::semantic_objects, not a bridge-local duplicate.

See Rust-Lean Bridge and Parity for details.

telltale-transport

Reference TCP transport implementation for choreography topologies.

Key exports:

  • TcpTransport, TcpTransportConfig, TransportState
  • Resolver and factory surfaces: EnvResolver, StaticResolver, TcpTransportFactory
  • Re-exported transport traits/types: Transport, TransportError, TransportResult, RoleName

For the transport-contract boundary example, run cargo run -p telltale-transport --example theorem_admission. The example does not open sockets. It shows that pre-shared-key TCP exports an authenticated runtime contract and trusted-network TCP fails theorem admission on authenticated_peers.

TLS is intentionally out of scope for this reference transport. Production deployments that require confidentiality or certificate-bound identity should wrap or replace it and export equivalent semantic contract evidence.

See rust/transport/src/lib.rs for the current public surface.

Guidance

When you need an exact signature, open the crate lib.rs and follow re-exports to the module definition. This keeps the reference accurate as the API evolves.

Verification Inventory

Lean metrics check mode compares generated source counts without modifying the tracked code map or renewing its editorial date. The minimal-environment gate also exercises an unchanged historical date and actual source-count drift. Failed checks must leave the original code map byte-for-byte unchanged.

The pinned Nix development shell provides ripgrep for the Lean proof-audit scripts in the canonical fast-structure lane. Release validation must not rely on an undeclared host installation of this required tool.

This page is the authoritative inventory for verification counts. Only counts that are stable enough to check automatically are tracked here.

The goal is to track coverage of key system properties and regression gates, not raw unit-test volume.

When one of these values changes legitimately:

  1. update the underlying source of truth,
  2. refresh this page,
  3. rerun just check-verification-inventory.

The numeric rows in this section are source-derived and checked by toolkit/xtask/src/checks/verification_inventory.rs.

MetricValueSource
Lean core-library files767lean/CODE_MAP.md total row
Lean core-library lines152,326lean/CODE_MAP.md total row
Lean-backed search fairness inventory entries56lean/Runtime/Proofs/Search/Inventory.lean
Ownership contract gate commands7just check-ownership-contracts
Aura-derived boundary checks9just check-aura-borrowed-lints
Explicit failure/timeout observable event kinds5rust/machine/src/engine/protocol_machine_config.rs (ObsEvent)
Macro UI pass fixtures11rust/macros/tests/macro_ui.rs
Macro UI compile-fail fixtures13rust/macros/tests/macro_ui.rs
Property buckets with executable assurance suites22Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Property buckets currently lacking executable assurance suites0Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Authority and ownership semantic assurance suites2Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Lean-backed correspondence strict suites8Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Identity and replay semantic assurance suites5Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Commitment and progress semantic assurance suites4Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Cross-mode semantic parity suites4Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Fail-closed lowering and admission gate suites5Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Structural locality and reconfiguration executable assurance suites5Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Semantic lifecycle invariant suites1Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Deterministic adversarial lifecycle scenarios10Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
End-to-end DSL runtime semantic conformance suites1Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Simulator semantic contract categories enforced automatically6Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Theorem-pack and admission executable assurance suites4Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Distributed and topology semantic harness suites3Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Agreement and composition runtime semantic suites4Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Extension and middleware semantic hardening suites2Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Generated topology and transport public-path suites1Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Runtime substrate boundary assurance suites2Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Handler contract boundary assurance suites2Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Long-horizon recovery differential harness suites1Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Artifact and release assurance suites4Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Mutation fail-closed assurance suites2Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Concrete protocol-machine refinement suites3Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Compiler and serialization pipeline suites5Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Deadlock automation fragment assurance suites3Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Docs-as-contract assurance suites3Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Deterministic scale and budget assurance suites1Curated property-suite map in toolkit/xtask/src/checks/verification_inventory.rs
Explicit unsupported or fail-closed property notes0Curated unsupported-surface note list in toolkit/xtask/src/checks/verification_inventory.rs

Current Formal Verification Claim

Telltale is formally verified for the declared shipped surface documented in this inventory, under the assumptions listed in the public TCB ledger below.

The exact public claim is:

Telltale is formally verified for its declared shipped surface: the Lean session-type, projection, protocol-machine, and conservation-property models and theorems, the theorem-defined Rust↔Lean protocol-machine runtime correspondence core for the audited supported corpus, the shipped first-party handler/transport boundary as documented by machine-checkable contract profiles, and the shipped first-party crate artifacts through the audited artifact-correspondence pipeline.

This does not mean every Rust API, compiler helper, macro path, plugin, or third-party integration is mechanically proved. Anything outside the declared shipped surface is listed explicitly below and is not part of the formal claim.

The runtime heap now has a focused Lean parity lane for canonical bytes, tagged preimages, ordering, proof-path structure, and deterministic replay. Its digest algorithm remains an operational conformance surface checked through published vectors and runtime tests rather than a first-class Lean hash model.

The simulator assurance story now includes explicit exact-backend parity, canonical replay reproduction, exact checkpoint resume, durable WAL fault injection and crash/recovery matrices, batch-order reproducibility, fail-closed theorem-classification matrices, approximation admissibility gates, nested distributed invariance, and helper-surface non-authoritativeness checks. These remain executable assurance lanes rather than part of the current mechanized formal claim.

The search crate now has a scoped Lean fairness lane as well. The current proved search fairness surface is:

  • canonical serial search is exact one-step fair for the current legal min-key batch
  • canonical serial search now has an executable reduced machine semantics plus an explicit reduced Rust-facing state/artifact projection boundary
  • canonical serial search now also has an executable full-machine semantics, an exported full-state artifact projection, and premise-scoped full-step refinement contracts back into the reduced executable lane
  • canonical serial search now also has a reduced machine-level invariant and projection layer, including executable machine-step invariant preservation, executable trace refinement into the packaged machine theorem surface, and a machine-trace restatement of current min-key eventual service
  • canonical serial search also has a dynamic liveness theorem under an explicit stability premise: if an entry remains continuously eligible and no strictly better entry appears, it is eventually serviced
  • canonical serial search has a fixed-phase termination theorem under an explicit finite reachable-node premise bundle
  • canonical serial search also has a rebuild-aware ARA-style termination theorem under an explicit lexicographic phase/work measure premise bundle
  • canonical serial search has both bounded strict-preemption and finite better-entry exhaustion theorems for entries that are not initially in the current min-key batch
  • canonical serial search now also has a scheduler-facing non-min service theorem whose public statement no longer exposes bounded-better-arrival or finite-better-universe terminology
  • canonical serial search has both frontier-level and machine-level witness-path goal-reachability theorems for the reduced model
  • canonical serial search now also has a graph-reachability-driven completeness theorem under explicit reachable-path, finiteness, and heuristic premises
  • canonical serial search now also has a raw-successor-semantics completeness theorem whose public statement quantifies over the domain successor contract instead of a user-supplied ready-path witness
  • canonical serial search also has an explicit machine-level bridge from goal reachability to incumbent publication under a publication premise bundle
  • canonical serial search now also has an eventual optimal-goal publication theorem under explicit admissibility and consistency premises
  • threaded exact single-lane search has the same exact one-step fairness through reduced one-step, commit-trace, state-slice, and observation-slice refinement theorems to canonical serial search
  • batched exact search has a certified-window fairness theorem with explicit premises plus a bounded dynamic starvation-freedom theorem under an explicit bounded-preemption premise, and its theorem-pack/export surface now also carries explicit support-class metadata alongside the certified multi-step window-trace validity theorem
  • envelope-bounded batched search now also has a certified-window theorem surface, theorem-backed normalized-commit observables, and a runtime certificate/export path aligned with the theorem pack

These search fairness claims are exposed operationally through the SearchFairnessArtifact and SearchFairnessCertificate runtime surfaces and checked by the dedicated just check-search-fairness gate, the Rust↔Lean search parity suite, and the verification-inventory gate. The current theorem pack is a first-class Lean artifact under Runtime.Proofs.Search.TheoremPack, with a mirrored Rust SearchTheoremPackArtifact for release-facing inventory export, now including per-theorem support classes that distinguish executable-semantics theorems, refinement corollaries, and premise-scoped theorems. Exact runtime runs now also export SearchStateArtifact plus per-round state/certificate traces. They also export SearchRouteBoundArtifact, whose current discovery surface is an observed run-scoped bound to first candidate publication, an observed recovery bound after the latest epoch transition, and a theorem-backed one-step goal-window service bound from the fairness certificate surface, now packaged as a structured SearchRouteDiscoveryCertificate and attached to the exact observed goal-window and publication steps for that run. The selected-route summary also carries a stable generic metric list alongside its scalar convenience fields. Its current quality surface remains profile-scoped rather than a separate Lean end-to-end discovery theorem. The release/provenance lane records the generated target/search-theorem-pack/search-theorem-pack.json artifact plus the generated canonical vector artifact target/search-artifacts/search-vectors-v1.json and the generated recovery vector artifact target/search-artifacts/search-recovery-vectors-v1.json. These claims are also backed by source-controlled search artifact vectors checked by the search_vectors and search_recovery_vectors conformance tests. They are still narrower than a blanket proof of fairness and completeness for all future frontier growth, all rebuild schedules, and all parallel modes without premises.

Claimed Surface

The current proved or proof-backed claimed surface is:

  • the Lean SessionTypes, SessionCoTypes, Choreography, Protocol, and Runtime models and theorem libraries
  • the strict Rust↔Lean correspondence corpora and comparison contracts tracked in the Lean correspondence rows below
  • the first-class protocol-critical capability/finalization/transition model carried by the Lean capability-model surface and its strict Rust↔Lean bridge correspondence suite
  • the packaged first-party crates and binaries only at the level of operationally checked artifact/release assurance, not full mechanized proof

Compiler and Macro Claim Boundary

The current public formal-verification claim does not include any Rust parser, lowering, projection, import/export, or macro-expansion entry path. More concretely: the current public formal-verification claim does not include any Rust parser, lowering, projection, import/export, or macro-expansion entry path.

For the current claim:

  • parse_choreography_str, parse_choreography_file, choreography_to_global, local_to_local_r, Rust-side projection/codegen/import-export helpers, and tell! macro expansion are outside the formal claim
  • because those entry paths are outside the current formal claim, no compiler-facing theorem-object JSON/import-export path remains on the current claim-critical path
  • those Rust compiler and macro paths are still covered by strict operational gates, especially the compiler-pipeline, projection-equivalence, exact-shape JSON, Lean-validator, and macro-UI suites
  • the exact DSL fragment that would need mechanized compiler proof before the public claim can broaden is the theory-convertible subset already tracked in the property table below: straight-line send/recv, communicated choice, call, counted loop, and guarded recursion
  • par, case/of, timeout, and any other fail-closed or runtime-only forms are outside both the current formal claim and that future proof-target subset

Artifact Correspondence Claim

The local release preflight checks its largest required disk reserve before expensive build lanes. An insufficient reserve fails the preflight without deleting build artifacts: Cargo targets may be shared with other workspaces or active builders. This resource failure is not a passing verification result. The preflight must be rerun after guarded cleanup by the artifact owner.

The manually dispatched Release Preflight workflow runs the same complete just ci-dry-run command in the pinned Nix environment on an isolated runner. Its evidence records the checked commit and full command log. A successful focused machine suite or a failed resource check does not substitute for this complete preflight. Publishing must use the exact checked source revision.

For the current public claim, the shipped first-party crate artifacts are covered only by operational artifact correspondence, not by mechanized proof.

More concretely, the current release/artifact claim is:

  • every publishable first-party crate tarball for the released version is built from the checked workspace manifests for that version
  • the packaged tarballs compiled by the artifact lane are the same tarballs whose hashes, sizes, git revision, toolchain versions, and critical embedded resource hashes are recorded in the provenance manifest under target/package-artifact-tarballs/provenance.json
  • critical embedded resources on the shipped path, including packaged README files, the embedded runtime grammar, and release metadata like the WASM example lockfile, are part of the audited artifact pipeline rather than an unchecked side channel

This is still narrower than a fully reproduced, mechanically proved binary build pipeline. Cargo, crates.io, git hosting, and the local build toolchain remain explicit external assumptions.

Out of Scope / Assumption Boundaries

First-class protocol-critical capability semantics are in scope. General host application authorization is out of scope.

The current public claim does not include:

  • Rust parser/lowering/projection/import-export and tell! macro expansion
  • user-supplied handlers, transports, plugins, or deployment adapters
  • third-party infrastructure or deployment environments
  • Cargo, crates.io, git hosting, OS, compiler, and toolchain correctness beyond the documented operational checks
  • cryptographic hardness assumptions beyond standard SHA-256-style assumptions
  • a blanket claim that every shipped Rust code path is mechanically proved
  • general host application authorization or arbitrary app-policy capability systems unrelated to protocol-critical semantics

Final Surface Audit

This table is the final public audit of which major surfaces are inside the formal claim and which are deliberately outside it.

SurfaceClaim statusNote
Lean SessionTypes, SessionCoTypes, Choreography, Protocol, and Runtime semantics/theoremsinsideCore mechanized proof surface
Theorem-defined protocol-machine runtime core (ConcreteScheduledStep, claimed state/transition/event slice)insideExact Rust↔Lean refinement/correspondence surface
First-party documented handlers (InMemoryHandler, TelltaleHandler, middleware profiles)insideIncluded through machine-checkable contract profiles
First-party documented transports (InMemoryChannelTransport, runtime topology TCP helper, telltale-transport TCP transport)insideIncluded through machine-checkable contract profiles
First-party packaged crate tarballs and embedded resourcesinsideIncluded through the operational artifact-correspondence and provenance pipeline
Rust parser, lowering, projection, import/export helpersoutsidePublicly supported but outside the formal claim and guarded operationally
tell! and other proc-macro convenience surfacesoutsidePublicly supported but outside the formal claim and guarded operationally
User-supplied third-party handlers, transports, plugins, and deployment adaptersoutsideRemain assumption-boundary integrations unless separately justified
External build/delivery infrastructure (cargo, crates.io, git hosting, toolchains, OS)assumption boundaryExplicit public TCB items rather than proved surfaces

Public TCB Ledger

The current trusted computing base for the public claim is:

ComponentWhy it remains trustedCurrent enforcement
Lean kernel and imported proof librariestheorem checker and proof environmentLean build + code map + proof targets
Lean model definitionstheorems are only as correct as the modeled semanticsLean proof suites and docs inventory
Classical real-analysis APIexternal analytic laws used by transported classical familiesClassicalAnalysisAPI.lean, ClassicalAnalysisInstance.lean, and scripts/lean/check-classical-proof-audit.sh
Rust/Lean bridge normalization and interchangecomparison/equality surface between Rust and Leanjust check-bridge-normalization, strict correspondence suites
Rust runtime implementationshipped executable semantics are still comparison-checked, not fully provedstrict correspondence, semantic assurance, refinement slice
First-party handlers/transportsexternal impurity boundary for the runtimehandler-contract, transport-contract, and runtime-boundary suites
Release/package scripts and generated resourcesartifact identity path from workspace to published cratespackage-artifact, package-provenance, release-recovery, and docs-as-contract gates
Cargo / crates.io / git / toolchainsexternal delivery/build platformoperational checks only

If that TCB shrinks or the claim broadens, this section must be updated before a broader public wording is used.

Final Public Claim Text

Use this exact wording in docs, release material, and summaries:

Telltale is formally verified for the declared shipped surface documented in the verification inventory, under the listed public assumptions and TCB.

Supporting scope statement:

The declared shipped surface includes the Lean semantics/theorems, the theorem-defined Rust↔Lean protocol-machine runtime correspondence core, the shipped first-party handler/transport contract boundary, and the shipped first-party crate artifacts through the audited artifact-correspondence pipeline. It does not include Rust compiler/macro entry paths or third-party integrations unless stated otherwise.

Supporting TCB statement:

The remaining public TCB consists of the Lean kernel and imported proof libraries, the minimized Rust/Lean bridge/interchange layer, the comparison- checked Rust runtime implementation for the claim-critical surface, the documented first-party handler/transport boundary, the audited release/package and generated-resource pipeline, and external build/delivery infrastructure such as Cargo, crates.io, git hosting, and the underlying toolchains.

Refinement Coverage Map

This map names the current protocol-machine state surfaces that matter to the claimed runtime story and shows whether they are already inside the exact Rust↔Lean refinement slice.

Runtime state componentRust surfaceLean surfaceCurrent refinement statusNote
Coroutine identity, program counter, status, owned-endpoint count, progress-token countrust/machine/src/refinement.rs (CoroutineRefinementSlice)lean/Runtime/Proofs/ProtocolMachine/ConcreteRefinement.lean (ConcreteCoroutineSlice)exact sliceCompared exactly across cooperative, Lean, and threaded executions today
Session id, role count, local-type entry count, buffer-edge count, buffered-message count, status tag, epochrust/machine/src/refinement.rs (SessionRefinementSlice)lean/Runtime/Proofs/ProtocolMachine/ConcreteRefinement.lean (ConcreteSessionSlice)exact sliceThe theorem-side session slice now carries the same session-status and buffer-edge surface used by the Rust runtime slice
Scheduler ready queue, blocked-set tags, step countrust/machine/src/refinement.rs (SchedulerRefinementSlice)lean/Runtime/Proofs/ProtocolMachine/ConcreteRefinement.lean (ConcreteSchedulerSlice)exact sliceThe canonical scheduler slice is compared exactly today
Per-step event stream, theorem-defined pre_state/post_state, selected coroutine/type state, session_type_counts, ready_queue, and blocked snapshotsrust/bridge/src/protocol_machine_runner.rs (ProtocolMachineStepState), rust/machine/src/refinement.rs (TransitionRefinementSummary, ClaimedRuntimeCoreBundle)lean/Runtime/Proofs/ProtocolMachine/ConcreteRefinement.lean, lean/Runtime/Tests/ProtocolMachineRunner.lean step-state JSON exporttheorem-defined claimed transition surface with exact strict correspondence to RustThe claim-critical transition object is now defined on the Lean side as a scheduled-step projection carrying pre/post slices, transition summary, and step event projection. Rust is compared against that exact surface, while compiler-layout-specific program counters remain exported for audit/debugging rather than as a separate semantic claim
Effect exchanges and output-condition tracerust/bridge/src/protocol_machine_runner.rs (effect_exchanges, output_condition_trace)Lean runner JSON export and strict bridge suitesoutside the current claim-critical refinement coreCompared in strict lanes, but the current mechanized runtime-refinement claim is intentionally scoped to the theorem-defined protocol-machine state/transition/event surface above
Semantic-object families (handoffs, progress contracts, publications, agreement state, transformation obligations)rust/machine/src/engine/runtime_exec/introspection.rs, rust/machine/src/threaded/runtime_introspection.rslean/Runtime/Proofs/ProtocolMachine/SemanticObjects/*theorem-backed separately, outside the current claim-critical refinement coreConservation theorems and strict comparison exist, but they are tracked as separate theorem families rather than folded into the concrete protocol-machine refinement core

Gate Ownership

The verification surface is organized around three canonical just-entry lanes. just ci-dry-run, check.yml, and verify.yml should call these names rather than duplicating their inner gate lists by hand.

GateCanonical entry pointPrimary owning filesLocal run surfaceGitHub run surface
Fast structural verificationjust check-fast-structurejustfile, check-formal-claim-scope, check-ci-assurance-lanes, toolkit/xtask/src/checks/verification_inventory.rs, toolkit/xtask/src/checks/bridge_normalization_ledger.rs, toolkit/xtask/src/checks/fail_closed_mutations.rs, check-source-doc-snippets, toolkit/xtask/src/checks/tooling_convergence.rs, Lean bootstrap scriptsjust check-pr-critical, just ci-dry-run, direct local recipe usecheck.yml, verify.yml
Focused assurancejust check-focused-assurancejustfile, strict Lean bridge suites, compiler pipeline suites, metatheory/refinement/runtime boundary suitesjust check-pr-critical, just ci-dry-run, direct local recipe usecheck.yml, verify.yml
Packaged artifact assurancejust check-package-artifactsjustfile, toolkit/xtask/src/checks/package_artifacts.rs, toolkit/xtask/src/checks/package_provenance.rs, toolkit/xtask/src/checks/durable_boundaries (via _toolkit-check), toolkit/xtask/src/checks/release_recovery.rsjust check-pr-critical, just ci-dry-run, direct local recipe usecheck.yml, verify.yml
PR-critical assurancejust check-pr-criticaljustfile, .github/workflows/check.yml, .github/workflows/verify.ymljust ci-dry-run, direct local recipe usecheck.yml, verify.yml
Scheduled deep assurancejust check-deep-assurancejustfile, .github/workflows/verify.yml, scale-budget and larger-corpus verification lanesjust ci-dry-run full, direct local recipe useverify.yml

For staged diffs restricted to the simulator subsystem, the narrower local gate is now just check-simulator-subsystem-staged. That path is intentionally local-only and does not replace the canonical repo-wide lanes above. It exists so simulator-only staged work still gets formatting, compile, test, and simulator-doc link enforcement without being blocked by unrelated pre-existing breakage elsewhere in the workspace.

The dedicated local search-fairness gate is just check-search-fairness. That gate is intentionally narrower than the canonical repo-wide lanes above and exists to keep the Lean theorem-pack, parity fixture, and inventory rows aligned while working inside telltale-search.

Property Coverage Baseline

This baseline records whether each conserved-property bucket has at least one high-signal executable assurance suite today. It is intentionally curated. The aim is to make gaps explicit rather than to produce vanity totals.

Property bucketExecutable statusHigh-signal suitesCurrent note
EvidenceSpot checksrust/machine/tests/ownership_contracts.rs, rust/machine/tests/conformance_lean.rsEvidence-bearing paths are exercised directly in runtime and Lean-backed spot checks. The current theorem-focused authority slice starts from authoritative reads plus canonical publication/materialization and the explicit finalization/canonical-handle subsystem rather than the whole authority lifecycle family
AuthorityCross-path checksrust/machine/tests/ownership_contracts.rs, rust/simulator/tests/ownership_faults.rsThe supported authority theorem slice is now explicit: evidence-bearing reads, transfer receipts, semantic handoff, and canonical publication/materialization are justified at the semantic-object and lifecycle layer, while broader host-policy lifecycle surfaces still rely on the wider protocol-machine conservation theorems
Lean correspondenceStrict executable validation checksrust/bridge/tests/lean_trace_validation.rs, rust/bridge/tests/property_tests.rs, rust/bridge/tests/protocol_bundle_admission_contracts.rs, rust/bridge/tests/protocol_machine_correspondence_tests.rs, rust/bridge/tests/protocol_machine_differential_steps.rs, rust/bridge/tests/capability_model_correspondence.rs, rust/bridge/tests/heap_lean_parity.rs, rust/simulator/tests/lean_reference_parity.rsvalidateTrace, validateSimulationTrace, runSimulation, verifyProtocolBundle, deterministic reconfiguration-transition validation, epoch-aware multi-step reconfiguration phase validation, explicit capability/finalization/transition-model correspondence, exact normalized simulator-trace parity, heap canonical-byte and replay parity, full protocol-machine event-stream parity, session-status parity, and step-indexed scheduler-state parity now execute in strict deterministic lanes for the supported corpus
IdentityCross-path checksrust/machine/tests/serialization_replay.rs, rust/machine/tests/replay_persistence_identity.rs, rust/bridge/tests/semantic_object_roundtrip.rs, rust/bridge/tests/protocol_machine_cross_target_tests.rs, rust/bridge/tests/reconfiguration_recovery_harness.rsReplay, snapshot/restore, canonical fragment roundtrip, semantic-object identity, ownership-transfer replay artifacts, and bridge-exported reconfiguration snapshot identity are now checked as one differential family across multiple surfaces
CommitmentSpot + path checksrust/machine/tests/unit/protocol_machine/tests_semantic_state.rs, rust/machine/tests/conformance_lean.rs, rust/machine/tests/threaded_equivalence.rs, rust/machine/src/runtime_contracts.rsProgress and terminalization are covered across runtime contracts, lifecycle harnesses, and cross-driver parity
CommitmentDeterministic lifecycle harnessrust/machine/src/engine/runtime_exec/semantic_state.rsSeeded lifecycle and adversarial corpus now exercise terminalization, invalidation, proof-gaps, and progress escalation as one semantic state machine
StructureDeterministic runtime structure suiterust/machine/src/engine/runtime_exec/semantic_state.rs, rust/machine/tests/ownership_contracts.rs, rust/machine/src/composition.rs, rust/bridge/tests/protocol_bundle_admission_contracts.rs, rust/runtime/tests/generated_topology_public_path.rsStructural handoff locality, transformation obligations, pre-transfer witness invalidation, generated topology validation, executable region inheritance/conflict checks, bridge-to-runtime reconfiguration admission, atomic multi-step plan execution, canonical placement/transport-boundary phase artifacts, deterministic reconfiguration history, snapshot/restore state, and Lean-validated transition artifacts are now exercised on executable runtime paths
PremiseFail-closed + admission checksrust/machine/src/runtime_contracts.rs, rust/machine/src/composition.rs, rust/language/src/compiler/parser/mod.rs, rust/runtime/tests/authority_compile_fail.rs, rust/runtime/tests/authority_control_flow_corpus.rsAssumption-heavy paths are rejected or gated, and the authority/control-flow boundary is now exercised with deterministic accepted/rejected .tell and tell! fixtures
PremiseEnd-to-end supported/fail-closed loweringrust/tests/dsl_runtime_semantics_tests.rsThe theory-backed supported subset is explicit and executable: choice, call, counted loop, and recursion remain parser -> projection -> theory-conversion -> protocol-machine clean. par, case/of, and timeout stay outside that theory-convertible subset and are covered through the executable/runtime and boundary suites above
AdmissionTheorem-pack and bundle assurancerust/bridge/tests/protocol_bundle_admission_contracts.rs, rust/bridge/tests/invariant_verification.rs, rust/machine/src/runtime_contracts.rs, rust/tests/dsl_runtime_semantics_tests.rsProof-bundle declarations, capability drops, admission-gated runtime requests, and the explicit transported-theorem boundary ledger are now exercised end to end with stable diagnostics. Runtime-critical instantiated premises are separated from Lean-only assumption boundaries and black-box/background theorem inventory
Distributed topologyDeterministic harnessrust/simulator/tests/distributed.rs, rust/machine/tests/topology_effect_ingress.rs, rust/runtime/tests/generated_topology_public_path.rsDistributed replay, ordered topology ingress, generated helpers, and invalid placement rejection now run through executable runtime paths without ambient network dependency
Simulator replay and classification assuranceDeterministic executable regression netrust/simulator/tests/correctness_regression.rs, rust/simulator/tests/threaded_backend.rs, rust/simulator/tests/classification_assurance.rs, rust/simulator/tests/environment_models.rs, rust/simulator/tests/distributed.rs, rust/simulator/tests/durable_assurance.rsCanonical exact, threaded exact, canonical replay, exact checkpoint resume, durable WAL fault injection, crash/recovery coverage matrices, write-ahead and evidence-consistency monitors, fail-closed theorem eligibility, approximation admissibility, normalized observability boundaries, observed-only distributed manifests, and non-authoritative helper surfaces are now checked together as one simulator-facing assurance family
AgreementRuntime commitment semanticsrust/machine/src/effect/core_types.rs, rust/machine/src/semantic_objects.rs, rust/machine/tests/threaded_equivalence.rs, rust/tests/dsl_runtime_semantics_tests.rsAgreement profiles and child-effect rollups are checked as runtime semantics across scenario tables, lowering, and cross-driver parity
Extension boundaryDeterministic parse-to-runtime dispatch + middleware stacksrust/runtime/tests/extension_integration.rs, rust/runtime/tests/middleware_semantic_hardening.rsRegistered statement rules now parse into Protocol::Extension, lower into runtime extension effects, execute through deterministic extensible handlers, fail with stable diagnostics when handlers are missing or payloads are malformed, and remain middleware-safe under retry, metrics, trace, and seeded fault injection
Generated deployment pathPublic helper end-to-end executionrust/runtime/tests/generated_topology_public_path.rshandler(...), with_topology(...), and named inline topology helpers are exercised as public surfaces with real loopback remote transport, negative validation coverage, executable region semantics, preservation of inline role-family constraints, and a transport-agnostic runtime topology API
Runtime substrateTarget-aware wrapper contractsrust/runtime/tests/runtime_substrate_contracts.rs, rust/runtime/tests/wasm_compat.rsNative and WASM wrapper seams now have direct regression coverage for spawn, spawn_local, and deterministic clock/RNG discipline, and deterministic assurance suites are guarded against accidental SystemClock / SystemRng drift
Handler contract boundaryMachine-checkable contract profiles for first-party handlers and transports, plus fail-closed extension dispatchrust/runtime/tests/handler_contracts.rs, rust/runtime/tests/transport_contracts.rsChoreoHandler and the first-party transport families now have explicit machine-checkable contract ledgers that separate protocol-semantic obligations from policy choices, validate shipped production and harness profiles mechanically, and prove deterministic registered-only extension dispatch plus fail-closed unregistered behavior through runtime tests. User-supplied third-party handlers/transports remain outside the formal claim unless they separately satisfy the same contract
RecoveryLong-horizon differential harnessrust/bridge/tests/reconfiguration_recovery_harness.rsOwnership-transfer replay artifacts, bridge export/import, topology-derived placement artifacts, atomic multi-step reconfiguration plans, snapshot/restore recovery, and deterministic suffix replay now execute as one end-to-end recovery family with explicit divergence detection
Artifact / releasePackaged-crate, provenance, and resume verificationtoolkit/xtask/src/checks/package_artifacts.rs, toolkit/xtask/src/checks/package_provenance.rs, toolkit/xtask/src/checks/durable_boundaries (via _toolkit-check), toolkit/xtask/src/checks/release_recovery.rsEvery publishable crate now goes through the cargo publish --dry-run --locked --no-verify packaging path, package-manifest resource paths are checked before packaging, the full packaged crate set is compiled from extracted tarballs, critical embedded resources are compared byte-for-byte against source, external consumer canaries for telltale, telltale-runtime, and telltale-bridge run outside the workspace layout with exact last-line stdout assertions, a provenance manifest records tarball hashes plus source/resource/toolchain metadata for the packaged set, package-root resource escapes are fail-closed, the packaged WASM and embedded-grammar surfaces are verified explicitly, and release resume behavior is exercised under a deterministic fake cargo/git harness
Mutation pressureDirect fail-closed perturbation suitesrust/machine/src/runtime_contracts.rs, toolkit/xtask/src/checks/fail_closed_mutations.rsRepresentative bridge payload, theorem-boundary, source-derived docs-row, package-registry, package-manifest, package-resource, and inventory mutations are injected directly against the narrow owning gates so drift is rejected before broader integration lanes run
Concrete refinementExact cooperative/Lean/threaded state-slice parity plus Lean proof-connected slicerust/bridge/tests/protocol_machine_differential_steps.rs, rust/machine/tests/lean_protocol_machine_equivalence.rs, rust/machine/tests/threaded_equivalence.rsThe first concrete protocol-machine refinement slice now compares coroutine/session/scheduler state exactly across Rust, Lean, and canonical threaded execution, exports bounded u64 bridge fields fail-closed, and is connected to dedicated Lean refinement theorems over the same slice
Compiler / serialization pipelineStrict DSL-to-theory lowering, exact-shape JSON bridge, and Lean-backed projection acceptancerust/bridge/tests/compiler_pipeline_conformance.rs, rust/bridge/tests/projection_equivalence.rs, rust/bridge/tests/proptest_json_roundtrip.rs, rust/bridge/tests/lean_integration_tests.rs, rust/bridge/tests/merge_semantics_tests.rsThis pipeline is operationally checked, not part of the current formal claim: the supported DSL subset runs through parser -> protocol_to_global() / local_to_local_r() -> exact-shape import/export -> Lean projection export and validator acceptance in deterministic strict lanes, and bridge import rejects unknown fields fail-closed so schema drift cannot hide behind permissive parsing
Deadlock automationLean-sound regular-fragment checker mirrored into Rust diagnosticsrust/types/src/local.rs, rust/bridge/tests/regular_practical_fragment_checks.rs, rust/tests/dsl_runtime_semantics_tests.rsThe automatic deadlock-discharge fragment is now mechanically characterized as closed + contractive projected locals whose full unfold exposes send/recv, checked first in Lean on SessionTypes.LocalTypeR, mirrored in Rust only for macro/proof-status diagnostics, and exercised end to end through bridge parity and generated proof_status surfaces
Public docs as contractSource-derived capability/admission, authority-support, and trust-surface tablesrust/bridge/tests/docs_contract_tests.rs, toolkit/xtask/src/checks/verification_inventory.rs, toolkit/xtask/src/checks/bridge_normalization_ledger.rsThe highest-value public verification/capability docs now separate source-derived tables from explanatory prose, and those rows are checked mechanically against runtime-contract, DSL-tier, and bridge-ledger facts
Deterministic scale budgetsLarger supported corpora with structural size envelopesrust/bridge/tests/scale_budget_contracts.rsLarger lowering/projection corpora, longer record/replay histories, larger reconfiguration snapshots, and larger Lean bridge payloads now run as deterministic structural-budget gates with exact replay/snapshot equality and explicit serialized-size envelopes rather than ambient wall-clock benchmarks

Bridge Normalization Trust Surface

The Lean bridge still contains a small amount of trusted normalization logic. That logic is intentionally narrow and is audited explicitly in CI by just check-bridge-normalization. The rows in this table are source-derived and checked by telltale_bridge::bridge_normalization_ledger() via toolkit/xtask/src/checks/bridge_normalization_ledger.rs.

SurfaceNormalization ruleClassificationWhy permitted
semantic-audit tick normalizationNormalize only tick, and only per extracted session idirreducible trusted comparison logicAbsolute cross-session scheduling order is not semantic protocol truth. Per-session observable order is.
single nominal occurrence comparisonRelate only the validated original nominal occurrence ID and preserve exact original-or-plus-one reply relation/refusal; never normalize ticks, typed application results, lifecycle or continuationirreducible trusted comparison logicRust also issues non-nominal observational effects, so its absolute counter namespace differs from the selected executable model. Each side must retain exactly one actual nominal occurrence with consistent operation/request references before the comparison relates that ID. Raw outputs are unchanged.

Enforcing artifacts:

  • rust/bridge/src/protocol_machine_trace.rs
  • rust/bridge/tests/protocol_machine_correspondence_tests.rs
  • rust/bridge/tests/protocol_machine_differential_steps.rs Session-status ordering is no longer part of the trusted comparison surface. Both the Lean runner and Rust-side correspondence harness now emit session rows in canonical sid order, so comparison is exact rather than normalized.

The older schema_version backfill helper remains only as a test-only compatibility shim for legacy fixture coverage. It is outside the claim-critical bridge surface and must not synthesize semantic content.

Explicit Unsupported / Fail-Closed Notes

No explicit unsupported or fail-closed implementation-gap notes remain in the tracked inventory. New notes should be added here only for intentional, documented non-goals or for temporary regressions that are not yet executable.

The inventory deliberately does not track raw unit-test totals, assertion counts, or line counts for tests.

Required Targeted Disposal Evidence

just check-session-disposal validates ten named native lifecycle regressions through Rust AST inventory, actual Cargo harness discovery with multi-thread, and successful execution of every required name. Missing, ignored, nested conditional ignore, wrong-module, and zero-test outcomes fail the gate. just check-ownership-contracts includes this gate in the PR-critical lane.

Prepared readiness lowering inventory

The authoritative nominal readiness child declares three portable machine codec/shape fixtures and six actual source/runtime fixture families for each backend in rust/tests/nominal_command_runtime.rs. Cooperative fixtures are portable, while threaded fixtures require multi-thread and non-WASM. They cover original registry single-use after actual rollback, real Blocked→between-round registry issuance→same-original retry, malformed/Boolean/predicate/reused evidence, genuine foreign session and committed transfer refusal, and actual callback-before-output-gate refusal with the witness still unconsumed. Same original native worker shutdown is explicit in the threaded fixtures. Discovery and execution remain required. Source declarations are not passing evidence.

NominalCommandExecution.lean adds eight named readiness examples over the actual installed nominal opcode/canonical commit or direct canonical model owner/issuer/consumer, including stale installed original owner before issuance and changed generation before retained-result reuse. They remain unelaborated source. The general source-to-model bridge must adapt explicitly to the typed result before claiming readiness correspondence. The transport model deliberately refuses readiness decoding. Witness transport is outside this terminal-unit continuation slice. Neither model state nor serialized witness data reconstructs Rust host authority.

Prepared original-operation checkpoint inventory

The existing dsl_runtime_semantics_tests target includes operation lifecycle, joint readiness, compiler-obligation refusal and central fault/Halt modules. Native threaded declarations require the root multi-thread feature. Same original worker disposal precedes final assertions. Existing bridge protocol_machine_differential_steps additionally declares actual source-parent Bool/result/Blocked/failure correspondence, parent/readiness output-gate correspondence and parent/child/window omission mutations. These use the existing required Lean runner and refuse its absence.

The targeted model prerequisite explicitly builds OperationLifecycleExecution, OperationReadinessExecution and OperationFailureExecution alongside the existing nominal modules. Examples execute installed typed opcodes and canonical owner/issuer/commit producers. Declarations remain unelaborated and Rust fixtures undiscovered/unexecuted. Genuine dependent-work AwaitPending, handoff, timeout/cancellation escalation and complete authority-family proofs remain open. No source-only count is acceptance evidence.

Glossary and Notation Index

This page defines shared terms and symbols used across the documentation and paper-aligned pages. It serves as the stable lookup for terminology and notation.

Core Terms

TermMeaningPrimary Docs
coherenceSession-wide compatibility invariant between local type state, buffers, and global structure.Theory, Theorem Program
harmonyProjection and protocol evolution commute under declared premises.Theory, Theorem Program
projectionMapping from global choreography to per-role local session types.Choreographic Projection Patterns
local typePer-role protocol view used for runtime typing and progression.Theory, Session Lifecycle
effect handlerRuntime boundary that interprets protocol-machine or choreography actions.Choreography Effect Handlers, Effect Handlers and Session Types
theorem-packLean-exported capability inventory used by runtime admission gates.Lean Verification, Capability Admission
capability taxonomyThe four first-class protocol-critical capability classes: admission, ownership, evidence, transition.Capability Model, Authority Language Surface
admissionRuntime gate process that checks contracts and capability evidence.Capability Admission
ownership capabilityRuntime host authority token carrying owner label, generation, and scope.Effect Handlers and Session Types, Session Lifecycle
evidenceTyped proof object or witness consumed by protocol-critical authority flow.Authority Language Surface
receiptSingle-use transfer or handoff proof emitted by an explicit ownership/delegation path.Session Lifecycle, Authority Language Surface
finalization subsystemThe explicit runtime/Lean model that classifies observed, authoritative, materialized, canonical, invalidated, and rejected semantic paths.Protocol Machine Architecture, Capability Model
canonical handleNon-forgeable runtime reference proving a materialized/publication path is consumable as canonical truth.Authority Language Surface, Capability Model
transition artifactReceipt, handoff, or reconfiguration/runtime-upgrade object that carries explicit transfer or cutover authority.Capability Model, Authority Language Surface
linear bindingBinding that the compiler requires to be consumed exactly once.Choreographic DSL, Authority Language Surface
ResultBuilt-in success/failure sum form with Ok and Err.Choreographic DSL
MaybeBuilt-in optional-value sum form with Just and Nothing.Choreographic DSL
effect declarationNominal choreography declaration for one typed external host interface.Authority Language Surface
uses clauseProtocol-level declaration of which named effect interfaces may be invoked.Authority Language Surface
timeout branchProtocol-visible timeout/cancellation construct with explicit alternate branches.Choreographic DSL, Authority Language Surface
ownership epochGeneration used to invalidate stale owner handles after transfer or scope attenuation.Session Lifecycle
canonical ingressSanctioned host event entry path such as topology_events, send_decision, handle_recv, or step.Effect Handlers and Session Types
stale-owner rejectionFail-closed behavior when a prior ownership capability is reused after transfer or attenuation.Effect Handlers and Session Types, Session Lifecycle
envelopeDeclared refinement boundary for higher-concurrency and profile-scoped behavior.Protocol Machine Architecture, Rust-Lean Bridge and Parity
determinism profileRuntime trace-equivalence contract mode such as Full or Replay.Protocol Machine Architecture, Rust-Lean Bridge and Parity
communication replay modeTransport replay-consumption policy: off, sequence, or nullifier.Protocol Machine Architecture, Session Lifecycle
communication nullifierDomain-separated digest of canonical communication identity used for one-time receive consumption checks.Protocol Machine Architecture, Session Lifecycle
consumption rootDeterministic accumulator root over communication replay-consumption state.Protocol Machine Architecture, Rust-Lean Bridge and Parity
protocol machineSingle-thread execution engine (ProtocolMachine) that runs projected local types with session type monitoring.Protocol Machine Architecture
guest runtimeTelltale-owned driver (GuestRuntime) instantiated around the protocol machine for simulation and runtime integration.Protocol Machine Architecture, API Reference
effect handlerHost-runtime boundary trait (EffectHandler) implemented by embedders and simulators.Effect Handlers and Session Types, API Reference
semantic objectTyped introspection record (such as OperationInstance, OutstandingEffect, CanonicalHandle) maintained by the protocol machine for audit and replay.Protocol Machine Architecture, API Reference
typed outcomeStructured success/failure result at the effect boundary using EffectResult and EffectFailure rather than raw strings.Capability Model, Authority Language Surface
content addressingCryptographic identity scheme (ContentId) for protocol artifacts enabling deduplication and integrity checks.API Reference
nominal effect interfaceNamed effect declaration (effect Name) that makes host dependencies explicit and typed at the language level.Authority Language Surface, Capability Model
conservation frameworkThe organizing design principle: all semantically meaningful behavior is expressed in terms of six conserved quantities.Conservation Framework
evidence (conserved property)The integrity of what has been established. Witnesses, proofs, and attestations are its concrete forms.Conservation Framework
authority (conserved property)The exclusivity of who may act. Ownership is its concrete form.Conservation Framework
identity (conserved property)The definiteness of object references across retries, effects, and handoffs.Conservation Framework
commitment (conserved property)The account of outstanding obligations. Outstanding effects are its concrete form.Conservation Framework
structure (conserved property)The essential shape of the protocol. Multiparty session types are its concrete form.Conservation Framework, Theory
premise (conserved property)The explicitness of environmental assumptions. Failure models and fairness assumptions are its concrete forms.Conservation Framework
erasure principleBehavior not part of the conserved quantities is not part of the programming model.Conservation Framework
reduction principleAll runtime behavior must reduce to the conservation framework.Conservation Framework
regionLocality and framing domain for structured coordination with lifecycle, authority, and observation boundaries.Conservation Framework
semantic core objectsThe closed set of protocol-visible objects: Region, OperationInstance, OutstandingEffect, SemanticHandoff, AuthoritativeRead, ObservedRead, MaterializationProof, CanonicalHandle, ProgressContract.Conservation Framework, Protocol Machine Architecture

Symbol and Notation Index

Symbol or FormMeaningPrimary Docs
GGlobal protocol type.Theory
L or LocalTypeRLocal role protocol type.Theory, Protocol-Machine Bytecode Instructions
project(G, R)Projection of global type G for role R.Theory, Choreographic Projection Patterns
μX. ... XRecursive protocol form with bound variable X.Theory
⊕{...}Internal choice at the selecting endpoint.Theory
&{...}External choice at the receiving endpoint.Theory
!T.SSend T, then continue as S.Theory
?T.SReceive T, then continue as S.Theory
endSession termination state.Theory
ConsumeRecursive receiver-side trace alignment kernel used in coherence proofs.Theory, Theorem Program
n = 1Canonical single-step concurrency regime for exact parity.Protocol Machine Architecture, Rust-Lean Bridge and Parity
n > 1Higher-concurrency regime admitted under envelope and premise-scoped constraints.Protocol Machine Architecture, Rust-Lean Bridge and Parity
Full, ModuloEffects, ModuloCommutativity, ReplayRuntime determinism profiles.Protocol Machine Architecture, Rust-Lean Bridge and Parity
off, sequence, nullifierCommunication replay-consumption modes.Protocol Machine Architecture, Session Lifecycle
telltale.comm.identity.v1Domain-separation tag for canonical communication identity schema.Protocol Machine Architecture
case ... ofExhaustive sum-pattern branching over forms such as Result and Maybe.Choreographic DSL, Authority Language Surface
let x = check Effect.op(args)Typed external query binding that later lowers to the protocol-machine effect boundary.Choreographic DSL, Authority Language Surface
effect NameNominal effect-interface declaration.Authority Language Surface
protocol P uses Runtime, AuditProtocol declaration naming its allowed effect interfaces.Authority Language Surface
timeout 5s R at ... on timeout => ... on cancel => ...Protocol-visible timeout and cancellation branch form.Choreographic DSL, Authority Language Surface

Notation Consistency Rules

Use one symbol for one concept in a local section. Reintroduce symbols when crossing between theory and runtime notation. Prefer existing symbols from this index unless precision requires a different one.