Stone is a Racket framework for building LLM ashlars 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.
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 events |
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 supervisor |
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 an ashlar |
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 ashlar |
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 an ashlar |
4.7.1 Prerequisites |
4.7.2 1. Add an ask-human ashlar |
4.7.3 2. Run under a record and wire a supervisor |
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. Decline a pending question |
4.7.8 7. Test with a scripted supervisor |
4.7.9 Troubleshooting |
4.7.10 See also |
4.8 Run a durable, resumable session |
4.8.1 Record, journal, context — three distinct things |
4.8.2 1. Open a record |
4.8.3 2. Run durably |
4.8.4 3. Resume after a crash |
4.8.5 4. Session loop: run until a terminal halt |
4.8.6 5. Sessions on the command line |
4.8.7 See also |
4.9 Handle failures in an ashlar |
4.9.1 Prerequisites |
4.9.2 1. Return a failure node from your ashlar |
4.9.3 2. Understand what propagates when |
4.9.4 3. Check for failure at the top level |
4.9.5 4. Retry with ashlar-loop |
4.9.6 5. Recover by folding repair into the loop body |
4.9.7 6. Retry on body failure, not just predicate miss |
4.9.8 7. Read failure events from the trace |
4.9.9 8. Fail fast vs recover |
4.9.10 Troubleshooting |
4.9.11 See also |
4.10 Configure a caller |
4.10.1 Prerequisites |
4.10.2 1. OpenAI-compatible servers |
4.10.3 2. Anthropic |
4.10.4 3. Disable Qwen3.5 thinking mode |
4.10.5 4. Provider-specific reasoning controls |
4.10.6 5. Reserved keys |
4.10.7 6. Multiple callers in one ashlar |
4.10.8 Troubleshooting |
4.10.9 See also |
4.11 Use the TUI |
4.11.1 Prerequisites |
4.11.2 1. Launch the TUI |
4.11.3 2. Panel layout |
4.11.4 3. Keybindings |
4.11.4.1 Global |
4.11.4.2 Normal mode |
4.11.4.3 Input mode |
4.11.4.4 Modal mode |
4.11.5 4. Answer an ask-human prompt |
4.11.6 5. Cancel, decline, and quit |
4.11.7 6. Resume a session |
4.11.8 Troubleshooting |
4.11.9 See also |
4.12 Test ashlars that use tools |
4.12.1 Prerequisites |
4.12.2 1. Live test: does this ashlar actually call ask_user? |
4.12.3 2. Mock test: ashlar wiring without network calls |
4.12.4 3. Catch accidentally-unstubbed tools with #:strict-tools? |
4.12.5 4. Bound test runtime with #:timeout |
4.12.6 5. Reference: which assertion should I use? |
4.12.7 6. Stub helpers |
4.12.8 7. Response builders |
4.12.9 8. When to use live vs. mock |
4.12.10 Troubleshooting |
4.12.11 See also |
4.13 Use the ashlar skills |
4.13.1 Prerequisites |
4.13.2 1. Install the skills |
4.13.3 2. The two-skill flow |
4.13.4 3. What scope-ashlar writes |
4.13.5 4. What build-ashlar generates |
4.13.6 5. When NOT to use these skills |
4.13.7 See also |
4.14 Coming from LangChain / LlamaIndex / bare SDK |
4.14.1 If you’re coming from LangChain / LangGraph |
4.14.1.1 State |
4.14.1.2 Edges and routing |
4.14.1.3 Chains |
4.14.1.4 Agents |
4.14.1.5 Streaming |
4.14.2 If you’re coming from LlamaIndex |
4.14.3 If you’re coming from a bare SDK |
4.14.3.1 Mapping SDK calls to Stone |
4.14.4 Racket-specific things LangChain doesn’t have |
4.14.5 Why you’d choose Stone |
4.14.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/context-struct |
5.6 stone/threshold |
5.6.1 Outside: the supervisor |
5.6.2 Inside: the engine |
5.7 stone/record |
5.7.1 stone/journal |
5.7.2 stone/tui-main |
5.8 stone/llm-types |
5.8.1 Responses |
5.8.2 Exceptions |
5.9 stone/llm-client |
5.9.1 Exceptions raised by the OpenAI caller |
5.10 stone/repetition-watch |
5.10.1 Hits |
5.10.2 Karp–Rabin n-gram counter |
5.10.3 Compression-ratio detector |
5.10.4 Combined watcher |
5.11 stone/tools |
5.11.1 Tool middleware |
5.11.2 Built-in tool middleware |
5.12 stone/decisions |
5.13 stone/validate |
5.14 stone/test |
5.14.1 Harnesses |
5.14.2 Assertions |
5.14.3 Stub helpers |
5.14.4 Response builders |
5.14.5 Parameters |
5.15 stone/logging |
5.16 stone/trace |
5.16.1 Loading |
5.16.2 Event accessors |
5.16.3 Aggregators |
5.16.4 Formatting |
5.16.5 CLI entry points |
5.17 stone/lens |
5.18 stone |
5.18.1 Parameters |
5.18.2 Schema builder |
5.18.3 Middleware onion types |
5.19 Command-line interface |
5.19.1 raco stone |
5.19.1.1 Configuration |
5.19.1.2 Sessions |
5.19.1.3 Exit codes |
5.19.2 raco stone sessions |
5.19.3 raco stone validate |
5.19.3.1 Ashlar file requirements |
5.19.3.2 Validation categories |
5.19.3.3 Output |
5.19.3.4 Exit codes |
5.19.4 raco stone trace |
5.19.4.1 payload options |
5.19.4.2 Exit codes |
5.19.5 raco stone install-skill |
5.19.5.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 Ashlar State |
6.2.1 Why a DAG instead of an ashlar 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 ashlars |
6.5 Ask Human |
6.5.1 Humans as ashlar participants |
6.5.2 The ashlar: make-ask-human |
6.5.3 The threshold is the medium |
6.5.4 Attended vs. unattended |
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 |