Latticeflow: Making AI Agents Deterministic
AI coding agents are powerful and completely non-deterministic. Latticeflow wraps them in one: a single native binary that drives a ticket through a deterministic plan, implement, review, ship lifecycle - and only pays for an agent on the steps that actually need one. I use it to build almost everything else on this site.
The Problem
My earlier approach drove everything through a persistent chat session that re-read its whole prompt corpus on every action. One observed 7-ticket chain on TradeTrack2 took four hours and used about 190 million cache-read tokens. Roughly 76% of that was the chat re-reading the same 26k-token prompt corpus on every transition. I was using a chat loop as a state machine and paying the full corpus cost on every step.
That run happened on a subscription, not a metered API bill. I do not publish a dollar conversion because I do not have a sourced pricing assumption for it. The observed baseline is still useful: repeated orchestration context, not implementation, dominated the run.
What I Built
Latticeflow, a single-binary command-line tool called build that owns the full lifecycle of a development ticket. Point it at a Plane project, give it a ticket, and it drives that ticket through a deterministic state machine: plan the work, cut a git worktree, implement the changes, run a gate of automated checks, dispatch a review, and ship the result back to the target branch. The ticket only advances in Plane once it clears that gate, so the review step is enforcement, not a courtesy. Every phase either succeeds, fails with a classified exit code, or triggers a bounded rework loop. No daemon, no server, no shared state across invocations - the binary exits at the end of each verb.
Latticeflow replaced Claude-Config, an earlier corpus of markdown slash-commands that drove ticket lifecycles through a live Claude Code chat acting as runtime, state machine, and tool gateway all at once. That corpus shipped real tickets on real projects, TradeTrack2 among them, and it’s the reference implementation behind The Shape of Done. Latticeflow is the public name, build the binary, throughline-build the repo.
Benchmark Method
The baseline is an observed predecessor run: seven TradeTrack2 tickets, four hours, about 190 million cache-read tokens, with roughly 76% attributable to repeatedly loading the same 26k-token prompt corpus.
The redesign removes that repeated prompt and state-machine work from the chat loop. State transitions, gates, and Plane writes run as deterministic code. Agent sessions are reserved for plan, implement, and review. I define comparable task quality by the same standard on both sides: a ticket is not complete until it passes its configured gates and review.
The roughly 10x result is a directional comparison from field operation, not a paired experiment. I did not replay the same seven tickets against both systems under controlled provider, model, cache, and pricing conditions, and I do not have a publishable redesigned token total for that run. The evidence supports the architecture and the direction of the reduction, not a more precise claim.
Tech Stack
- C#
- .NET 10
- native AOT (single self-contained binary)
- System.Text.Json
- TOML config
- Plane backend (git worktrees for isolation)
- cross-platform AOT builds (Windows / macOS / Linux)
- GitHub Actions three-platform CI matrix
- multi-vendor worker orchestration - Claude Code
- Codex
- Gemini
- Copilot CLIs
- ConPTY / Unix PTY interactive transport
- xUnit
How It Works
C# on .NET 10, compiled to a native-AOT single executable that needs no .NET runtime on the target. Every interaction falls into one of three tiers: state transitions, gates, and Plane writes are plain deterministic code; small scoped decisions are single API calls; plan, implement, and review spawn a worker CLI in an isolated git worktree.
The worker layer is multi-vendor. Claude Code, Codex, Gemini, and Copilot each sit behind an IWorkerAgent implementation, selectable globally or per phase.
A check that always passes provides false confidence. Before Latticeflow trusts a gate, it confirms that each configured check can actually fail. The GateVacuityProver owns that proof. A separate GateControlProber re-runs failed checks against the untouched base ref so a broken environment is never blamed on the ticket.
The chain orchestrator drives multi-ticket trees through a post-order traversal with accumulated integration branches - children ship into a parent’s local chain branch, nested parents merge up, and only the outermost chain lands and pushes. Multi-ticket dispatch builds a graph from blocked_by relations and runs level-synchronously with bounded concurrency.
Vendor Compatibility
All four adapters are wired behind the same worker interface. The validation evidence is not identical, so the matrix keeps “wired” separate from “validated on every platform.”
| Adapter | Wired transport and auth posture | Published platform evidence |
|---|---|---|
| Claude Code | CLI worker; interactive subscription transport through ConPTY on Windows and a Unix PTY on macOS and Linux | Interactive transport validated on Windows 11, macOS 26.4 arm64, and Ubuntu 24.04 x86_64 against Claude 2.1.177 |
| Codex | CLI worker; API keys removed from the child environment so the CLI uses its own subscription auth | Adapter is wired; individual platform validation is not documented |
| Gemini | CLI worker; API keys removed from the child environment with ADC fallback | Adapter is wired; individual platform validation is not documented |
| Copilot | CLI worker; inherits the GitHub CLI keyring credential | Adapter is wired; individual platform validation is not documented |
Enterprise Operation and Governance
Latticeflow is operator-run today. Each worker CLI owns its authentication. The orchestrator removes provider API keys from child environments where required, while Plane credentials can be referenced through environment-variable indirection in local TOML configuration. Configuration validation fails early when a selected worker has no matching config block.
The production governance path is local configuration, per-phase provider selection, isolated worktrees, deterministic gates, bounded rework, classified exit codes, and Plane as the ticket system of record. A centralized enterprise provider account and consolidated provider audit trail are not the production path today. The vendor-neutral IModelClient with SSE streaming is built and tested, but it is not wired into production, so I do not present centralized API-account control as shipped.
What Made It Hard
Making a non-deterministic tool behave deterministically at the boundaries. Attributing cost per action across four different vendor CLIs. Proving a passing gate actually tested something. And building an interactive transport for Claude Code after Anthropic moved --print usage onto a separate credit allowance: launching Claude under a platform PTY (ConPTY on Windows, a Unix PTY elsewhere), feeding the brief as a file reference, and detecting completion by tailing the persisted transcript once the per-turn Stop hook stopped firing in interactive sessions. Validated green on Windows 11, macOS 26.4 arm64, and Ubuntu 24.04.
The Throughline
This is the tooling underneath everything: I built the system that builds the products. It’s the productized version of the method in The Shape of Done, writing to my self-hosted Plane fork, and it’s the difference between using AI and engineering with it.
Status
Active development, and dogfooded - every ticket in Latticeflow ships through Latticeflow. The four worker agents, the full plan-through-ship pipeline, the chain orchestrator with gate and batch implement, the bootstrap verbs, and the interactive Claude Code transport are all functional and in daily use, with the AOT binary built and tested across all three platforms. A vendor-neutral model client with SSE streaming is built and tested but not yet wired onto a production path. Plane is the sole ticketing backend today; GitHub and Linear remain aspirational.