Why modern C++ (not Rust)
agentty stays modern C++ and gets the guarantees people move to Rust for — at compile time, in the header. 151+ static_assert proofs, an enforced lock hierarchy, and structurally-isolated worker panics. The code is the argument.
This is not a language flame-war. It's a claim with receipts: agentty already gets the guarantees people move to Rust for — and it gets them at compile time, in the header, next to the code they constrain. The codebase is the argument.
fish moved C++ → Rust in 4.0. Good decision for fish. This page walks through those reasons and shows why they don't apply here — and where agentty's approach is sharper.
The receipts, up front
- 151+
static_assert/constevalproofs across the headers ininclude/agentty/, all evaluated every build. The build is the test runner: a greencmake --buildmeans every invariant below holds. There is no separate CI step that can be skipped, no fixture that can drift, no#[test]someone forgot to run. - The proofs live next to the code they constrain, so a reader sees the contract before the implementation.
fish's reasons, mapped onto agentty
"The C++ toolchain was painful on old / LTS systems." Real for a shell that ships in every distro's base image. agentty is an application built from a pinned toolchain — one cmake --build. Not our pain.
"Memory safety on untrusted input." A shell parses arbitrary command lines — a huge adversarial surface. agentty funnels its untrusted input (prompt, model output, tool results) through one thin dynamism boundary; past it every value is typed (std::expected<T,E>, closed variant state machines, tagged-type identities). The bugs that actually bit agentty in the field were protocol and rendering-model logic bugs — the class Rust's borrow checker does not catch — and agentty pins logic invariants at compile time.
"Cargo dependency management." A convenience, not a reason to rewrite a working, disciplined codebase.
"Rust's #[test] discipline." This is the crux, and it's where agentty is sharper: a Rust contributor proving a table correct writes #[test] and runs them in CI. agentty writes consteval predicates and static_asserts them — the proof runs every build, can't be skipped, and lives in the same header as the table. Same discipline, smaller blast radius.
The exhibit — proofs you can read in five minutes
tool/policy.hpp— the permission matrix(EffectSet × Profile) → Decisionis a pureconstexprfunction; every cell isstatic_asserted.provider/error_class.hpp—classify(HttpError) → ErrorClassis exhaustive on the enum and every retry-relevant status is pinned ({Status,429} == RateLimit,{Status,401} == Auth).tool/spec.hpp— the capability catalog proves name↔Kind bijection, "only known tools carry Exec", truncation-strategy correlation, and the one sanctioned scheduling divergence fortask, allconsteval.domain/refined.hpp— refinement types: aBounded<uint16_t,1,65535>port literally cannot hold 0 or 65536.util/env.hpp— the env-var enum and its string catalog are proven in bijection at build time.
Beyond parity: two places agentty does more than stock Rust
- Lock ordering is a type, not a comment. The ACP server's mutexes are
RankedMutex<10>(session) andRankedMutex<20>(thread); acquiring them out of order is astatic_assert(compile error) in one scope and a debug-timestd::aborttripwire across function boundaries. Rust's type system does not check lock ordering at all — deadlocks are memory-safe there. - Worker panics are structurally isolated. Turn workers spawn through a primitive whose body makes
std::terminateunreachable — one bad turn can never take down every session. The guarantee is in the spawn primitive, so it can't be forgotten per-site.
When moving would be right
Honesty matters or the argument is worthless. Move to Rust if you want a large external plugin ecosystem and C++ is scaring contributors away, if you start hitting genuine data races the borrow checker would prevent, or if you're doing a clean-slate rewrite anyway. None of those is true today.
✦TipThe full contract lives in the repo at docs/DESIGN.md; the honest opposition research (every point a Rust advocate can raise, and how each was closed) is in docs/RUST-CRITIQUE.md. The code is the argument — read the five headers above.