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 test canonical_parity_is_exact_at_concurrency_one enforces exact equality. The test envelope_bounded_parity_holds_for_n_gt_1 enforces envelope-bounded behavior.

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
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.

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.

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.

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.

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.

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.

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 artifact equality
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.Refinement proves that threadedExactSingleLane is identical to canonical serial at every reduced artifact boundary. These are equalities, not simulations: parallelisation of successor enumeration within a batch produces no observable difference in batch nodes, normalized commits, state, or observations.

  • 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.

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 with no internal preemption terminology exposed.

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 reachable-node path, finiteness, and heuristic assumptions.
  • 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 - under explicit admissibility and consistency premises, eventual goal publication strengthens to eventual optimal-goal publication.

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 and canonical_serial_goal_window_service_has_exact_suffix_bound - restate the Fairness results at profile granularity.
  • 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 match canonical serial exactly.
  • 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.

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.

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

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.

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

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 runtime-semantic-only forms are still executable and still covered by the protocol-machine conservation theorems. The important limit is narrower: they are not currently claimed as part of 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 bindingyesyesyesevidence_publication_semantic_objectsnone
counted loop with authoritative bindingyesyesyesevidence_publication_semantic_objectsnone
recursion with authoritative bindingyesyesyesevidence_publication_semantic_objectsnone
par with observational bindingyesyesnoruntime_semantic_onlyParallel
case/of with authoritative bindingyesyesnoruntime_semantic_onlyCase
timeout with observational bindingyesyesnoruntime_semantic_onlyTimeout

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.

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.

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-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

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.

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.

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.

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. 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.

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. 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 artifacts carry small semantic obligations such as Lyapunov descent, backpressure optimality, or mixing-time bounds. They do not assert stronger system-level claims beyond their declared assumptions.

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. 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.

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.

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.

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 and a replay trace. Report fields include the theorem inventory, a transformation-eligibility snapshot, replay conformance, cross-target failure-envelope witness presence, restart-adequacy witness presence, and single-thread, multi-thread, and sharded evidence flags. releaseBuildGate derives the build-gate Boolean from the report when release-tagged.

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

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 files701lean/CODE_MAP.md total row
Lean core-library lines141,990lean/CODE_MAP.md total row
Lean-backed search fairness inventory entries56lean/Runtime/Proofs/Search/Inventory.lean
Ownership contract gate commands6just 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

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.

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.

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.