On this page:
Stone
9.1

Stone🔗ℹ

Aldric Giacomoni

Stone is a Racket framework for building LLM pipelines that you can trace, validate, and compose. Every step is an ashlar; ashlars share a typed, append-only DAG; every run leaves behind a complete, content-addressed record of what happened.

This manual is organized into four parts. Read Why Stone first if you are new to the framework — it explains what Stone is for and when to reach for it. Work through the tutorials in order to learn by doing. Dip into the how-to guides when you have a specific task. Keep the reference open while you work. Read the explanations when you want the why behind a design choice.

    1 Why Stone

      1.1 What Stone is for

      1.2 Who Stone is for

      1.3 Against agentic assistants

      1.4 Against pipeline frameworks

      1.5 Against bare SDK calls

      1.6 What Stone does not do

      1.7 Where to go next

    2 Reading Racket

      2.1 Parentheses, prefix, and lambdas

      2.2 Keyword arguments

      2.3 Hashes

      2.4 Lists and quoting

      2.5 Parameters and dynamic extent

      2.6 define-values for multiple returns

      2.7 Modules and requires

      2.8 Threads and channels

      2.9 Tips for reading nested parentheses

      2.10 Where to go for more

    3 Tutorials

      3.1 Getting Started

        3.1.1 Prerequisites

        3.1.2 Step 1: Set up the file

        3.1.3 Step 2: Configure the caller

        3.1.4 Step 3: Write the seed ashlar

        3.1.5 Step 4: Write the LLM ashlar

        3.1.6 Step 5: Compose and run

        3.1.7 What you’ve built

        3.1.8 Next steps

          3.1.8.1 Switching providers

      3.2 Your First Orchestration

        3.2.1 Prerequisites

        3.2.2 Step 1: A schema for the structured output

        3.2.3 Step 2: A structured proposer ashlar

        3.2.4 Step 3: A completeness predicate

        3.2.5 Step 4: An ask-human ashlar

        3.2.6 Step 5: A stdin reader

        3.2.7 Step 6: Compose with ashlar-loop and ashlar-match

        3.2.8 Step 7: Run it

        3.2.9 What you’ve built

        3.2.10 Next steps

    4 How-to guides

      4.1 Debug a pipeline

        4.1.1 Prerequisites

        4.1.2 1. Enable a receiver

        4.1.3 2. Parameterize a trace id

        4.1.4 3. Read the stream

        4.1.5 4. Enable debug-level events when you need them

        4.1.6 5. Follow a specific trace-id

        4.1.7 6. Read failure events

        4.1.8 7. Check the DAG after a run

        4.1.9 8. Test-time interception

        4.1.10 Troubleshooting

        4.1.11 See also

      4.2 Trace a run

        4.2.1 Prerequisites

        4.2.2 Steps

          4.2.2.1 1. Require the logging pieces

          4.2.2.2 2. Open a trace file and a log receiver

          4.2.2.3 3. Start a background thread to write events

          4.2.2.4 4. Set trace-id and span-id around the run

          4.2.2.5 5. Run and inspect

        4.2.3 Reading the trace

        4.2.4 Troubleshooting

        4.2.5 See also

      4.3 Validate a topology

        4.3.1 Prerequisites

        4.3.2 1. Validate from the REPL or a script

        4.3.3 2. Validate from the command line

        4.3.4 3. Interpret common errors

          4.3.4.1 'missing-producer

          4.3.4.2 'maybe-unavailable

          4.3.4.3 'invalid-lens

          4.3.4.4 'fanout-not-reduced

        4.3.5 4. Integrate validation into your workflow

          4.3.5.1 As a pre-run check

          4.3.5.2 As a CI gate

          4.3.5.3 In tests

        4.3.6 5. Enumerate ashlars in a topology

          4.3.6.1 Orphan-ashlar guard

        4.3.7 Troubleshooting

        4.3.8 See also

      4.4 Write a custom ashlar

        4.4.1 Prerequisites

        4.4.2 1. The shape of a custom ashlar

        4.4.3 2. Reading from the DAG

        4.4.4 3. Producing a typed node

        4.4.5 4. Handling failure

        4.4.6 5. Side effects are fine

        4.4.7 6. Choosing the right constructor

        4.4.8 7. Declaring metadata correctly

        4.4.9 Troubleshooting

        4.4.10 See also

      4.5 Route on a node from earlier in the pipeline

        4.5.1 Prerequisites

        4.5.2 1. Use a procedure extractor, not a lens

        4.5.3 2. Walk to the classifier’s node

        4.5.4 3. Project the routing key with node-get

        4.5.5 Troubleshooting

        4.5.6 See also

      4.6 Add structured output to an LLM ashlar

        4.6.1 Prerequisites

        4.6.2 1. Define a schema

        4.6.3 2. Set #:response-format on the ashlar

        4.6.4 3. Read the structured output downstream

        4.6.5 4. Handle parse failures

        4.6.6 5. Fold a human step into the body when retries aren’t enough

        4.6.7 6. Auto-derived schema metadata

        4.6.8 7. Dispatching to different node shapes with #:finalize

        4.6.9 Troubleshooting

        4.6.10 See also

      4.7 Add human interaction to a pipeline

        4.7.1 Prerequisites

        4.7.2 1. Add an ask-human ashlar

        4.7.3 2. Wire a stdin frontend

        4.7.4 3. Use an approval loop pattern

        4.7.5 4. Use a learning loop pattern

        4.7.6 5. Branch on a human response

        4.7.7 6. Cancel a pending question

        4.7.8 7. Test with a scripted frontend

        4.7.9 Troubleshooting

        4.7.10 See also

      4.8 Handle failures in a pipeline

        4.8.1 Prerequisites

        4.8.2 1. Return a failure node from your ashlar

        4.8.3 2. Understand what propagates when

        4.8.4 3. Check for failure at the top level

        4.8.5 4. Retry with ashlar-loop

        4.8.6 5. Recover by folding repair into the loop body

        4.8.7 6. Retry on body failure, not just predicate miss

        4.8.8 7. Read failure events from the trace

        4.8.9 8. Fail fast vs recover

        4.8.10 Troubleshooting

        4.8.11 See also

      4.9 Configure a caller

        4.9.1 Prerequisites

        4.9.2 1. OpenAI-compatible servers

        4.9.3 2. Anthropic

        4.9.4 3. Disable Qwen3.5 thinking mode

        4.9.5 4. Provider-specific reasoning controls

        4.9.6 5. Reserved keys

        4.9.7 6. Multiple callers in one pipeline

        4.9.8 Troubleshooting

        4.9.9 See also

      4.10 Use the TUI

        4.10.1 Prerequisites

        4.10.2 1. Launch the TUI

        4.10.3 2. Panel layout

        4.10.4 3. Keybindings

          4.10.4.1 Global

          4.10.4.2 Normal mode

          4.10.4.3 Input mode

          4.10.4.4 Modal mode

        4.10.5 4. Answer an ask-human prompt

        4.10.6 5. Cancel a pending question

        4.10.7 6. Exit cleanly

        4.10.8 Troubleshooting

        4.10.9 See also

      4.11 Test ashlars that use tools

        4.11.1 Prerequisites

        4.11.2 1. Live test: does this ashlar actually call ask_user?

        4.11.3 2. Mock test: pipeline wiring without network calls

        4.11.4 3. Catch accidentally-unstubbed tools with #:strict-tools?

        4.11.5 4. Bound test runtime with #:timeout

        4.11.6 5. Reference: which assertion should I use?

        4.11.7 6. Stub helpers

        4.11.8 7. Response builders

        4.11.9 8. When to use live vs. mock

        4.11.10 Troubleshooting

        4.11.11 See also

      4.12 Use the designing-ashlars skill

        4.12.1 Prerequisites

        4.12.2 1. Install the skill

        4.12.3 2. What happens in a session

        4.12.4 3. What gets generated

        4.12.5 4. When NOT to use this skill

        4.12.6 See also

      4.13 Coming from LangChain / LlamaIndex / bare SDK

        4.13.1 If you’re coming from LangChain / LangGraph

          4.13.1.1 State

          4.13.1.2 Edges and routing

          4.13.1.3 Chains

          4.13.1.4 Agents

          4.13.1.5 Streaming

        4.13.2 If you’re coming from LlamaIndex

        4.13.3 If you’re coming from a bare SDK

          4.13.3.1 Mapping SDK calls to Stone

        4.13.4 Racket-specific things LangChain doesn’t have

        4.13.5 Why you’d choose Stone

        4.13.6 See also

    5 Reference

      5.1 stone/edge

      5.2 stone/dag

        5.2.1 Nodes

        5.2.2 DAGs

      5.3 stone/messages

      5.4 stone/llm-ashlar

      5.5 stone/llm-types

        5.5.1 Responses

        5.5.2 Exceptions

      5.6 stone/llm-client

        5.6.1 Exceptions raised by the OpenAI caller

      5.7 stone/repetition-watch

        5.7.1 Hits

        5.7.2 Karp–Rabin n-gram counter

        5.7.3 Compression-ratio detector

        5.7.4 Combined watcher

      5.8 stone/tools

        5.8.1 Tool middleware

        5.8.2 Built-in tool middleware

        5.8.3 Ask-human channels

      5.9 stone/decisions

      5.10 stone/validate

      5.11 stone/test

        5.11.1 Harnesses

        5.11.2 Assertions

        5.11.3 Stub helpers

        5.11.4 Response builders

        5.11.5 Parameters

      5.12 stone/logging

      5.13 stone/trace

        5.13.1 Loading

        5.13.2 Event accessors

        5.13.3 Aggregators

        5.13.4 Formatting

        5.13.5 CLI entry points

      5.14 stone/lens

      5.15 stone

        5.15.1 Parameters

        5.15.2 Schema builder

        5.15.3 Middleware onion types

      5.16 Command-line interface

        5.16.1 raco stone

          5.16.1.1 Configuration

          5.16.1.2 Exit codes

        5.16.2 raco stone validate

          5.16.2.1 Pipeline file requirements

          5.16.2.2 Validation categories

          5.16.2.3 Output

          5.16.2.4 Exit codes

        5.16.3 raco stone trace

          5.16.3.1 payload options

          5.16.3.2 Exit codes

        5.16.4 raco stone install-skill

          5.16.4.1 Exit codes

    6 Explanation

      6.1 Ashlars

        6.1.1 The atomic unit

        6.1.2 The DAG as shared state

        6.1.3 Why everything is an ashlar

        6.1.4 The constructors

        6.1.5 Failure as a value

        6.1.6 What this shape buys

      6.2 The DAG as Pipeline State

        6.2.1 Why a DAG instead of a pipeline of arguments

        6.2.2 Nodes are typed

        6.2.3 Querying by type, answered structurally

          6.2.3.1 Projection helpers

        6.2.4 Content-addressed and append-only

        6.2.5 Branching and merging

        6.2.6 Failure nodes

        6.2.7 What this shape buys

      6.3 Edge Primitives

        6.3.1 Why primitives at all

        6.3.2 ~> — the sequence

        6.3.3 ashlar-loop — bounded repetition with a predicate

        6.3.4 ashlar-match — conditional branching

          6.3.4.1 Extractors as projection over DAG state

        6.3.5 ashlar-map — data-dependent fan-out

        6.3.6 ashlar-parallel — static fan-out

        6.3.7 ashlar-reduce — the semantic marker

        6.3.8 make-ask-human — the topology-level ask

        6.3.9 How they compose

        6.3.10 Failure propagation

        6.3.11 Scope and rollup

      6.4 Agents and Tools

        6.4.1 Agent ashlars as ashlars

        6.4.2 Inside an agent ashlar

          6.4.2.1 The conversation is a list of messages, not a DAG

          6.4.2.2 The middleware/caller contract

          6.4.2.3 Provider-specific bits go in metadata

          6.4.2.4 One consequence for downstream consumers

        6.4.3 Middleware as capability composition

        6.4.4 Tools are middleware

        6.4.5 Decisions: when to stop

        6.4.6 The adversary and the healer

        6.4.7 Conversation-tail invariant

        6.4.8 Shaping the output: #:finalize

        6.4.9 Capabilities, not permissions

        6.4.10 Why this matters for pipelines

      6.5 Ask Human

        6.5.1 Humans as pipeline participants

        6.5.2 The ashlar: make-ask-human

        6.5.3 Channels are the medium

        6.5.4 Channels flow from builder to frontend as data

        6.5.5 The frontend is not part of the topology

        6.5.6 Composing human interaction

        6.5.7 Why not middleware for human input?

      6.6 Validation

        6.6.1 Validation as a compose-time check

        6.6.2 What metadata the validator reads

        6.6.3 Walk-rule dispatch

        6.6.4 What the validator catches

        6.6.5 What the validator doesn’t catch

        6.6.6 Enumeration

        6.6.7 Running validation

      6.7 Observability

        6.7.1 Observability as a module-level concern

        6.7.2 Using Racket’s logging

        6.7.3 Correlation via parameters

        6.7.4 Events carry typed data

        6.7.5 Receivers and the consumer pattern

        6.7.6 Testing with with-intercepted-logging

        6.7.7 Failures are auto-logged

        6.7.8 Streaming

      6.8 The test harness

        6.8.1 The problem: mocks can lie about LLM behavior

        6.8.2 Why intercept at tool dispatch

        6.8.3 Why stub by name, not by object identity

        6.8.4 Why rebuilder closures on ashlar-meta

        6.8.5 Why the recorder reuses stone-logger

        6.8.6 Why the recorder is synchronous

        6.8.7 Why two modes share one API

        6.8.8 What’s out of scope in v1

      6.9 The ashlar-pair pattern

        6.9.1 Why the combination breaks on vLLM

        6.9.2 The pattern: explorer + structurer

        6.9.3 When to split and when not to

        6.9.4 Trade-offs

        6.9.5 Related provider constraints

      6.10 Provider constraints

        6.10.1 Qwen3.5 on vLLM: thinking-by-default

        6.10.2 vLLM: schema and tools can’t co-exist in one call

        6.10.3 Anthropic: streaming isn’t implemented yet

        6.10.4 OpenAI-compatible providers: reasoning controls diverge

        6.10.5 Tool schemas: Anthropic shape in, provider format out

        6.10.6 What to do when you hit a provider wall

      6.11 Harness vs. pipeline

        6.11.1 What "agent harness" means in 2026

        6.11.2 Translation table

        6.11.3 Where Stone exceeds typical harnesses

        6.11.4 Where Stone is not a deployed harness

        6.11.5 See also