AI wrote my compiler. A mathematical proof checks its work on every build. A developer building Almide, a programming language designed for AI agents, found that syntax choices matter less than error recovery. By measuring Modification Survival Rate (MSR) — whether a model can fix its own code after seeing compiler errors — the project achieved 100% pass rates on 20 test problems with Claude models, outperforming mature languages. The key insight is that frontier models rarely get syntax wrong, so design should focus on helping models recover from mistakes. Four months ago I published a claim with weak evidence behind it: that how easy a language is for an AI to write correctly depends on the language's design, not on which model you throw at it. The evidence at the time was one toy language that passed all 8 of my test problems — but took 3x longer than Ruby to get there. My working theory was that the gap was just a training-data problem. Put more of this language's code on the internet, and the model would eventually catch up on its own. That didn't happen. There's barely more Almide code on the public internet today than there was four months ago. And Claude's current models now solve all 20 of my test problems on the first or second try — faster, and in fewer lines, than mature languages with a decade of training data behind them. Here's what actually changed instead. Almide is a language designed to be written by AI agents, and for the last 4.5 months I've barely touched its compiler's source myself. The commit log's Co-Authored-By: Claude trailers tell that story better than I can. Steering a fleet of stateless agents through a codebase this size needed a policy that survives the fact that none of them remember the last session. The fix was mundane: a file every agent reads before it does anything. Line one of CLAUDE.md : Every design decision serves one metric: modification survival rate. Modification Survival Rate MSR is not "did the model get it right on the first try." It's: let the model write code, compile it, and if compilation fails, hand it the raw error message and let it retry — up to 3 times. What gets measured is whether the loop converges on working code, not whether the first draft was perfect. Model writes code → compile succeeds → pass fails → show raw compiler error → model retries → compile again after 3 failed retries → fail That framing — score the retry loop, not the first draft — turned out to be the single highest-leverage decision in this project. It means every syntax choice, every error message, every stdlib function name gets judged by one question: does this help the model recover from being wrong, not just avoid being wrong in the first place. Once MSR was the metric, syntax debates stopped being taste and became measurable. The first one: should a lambda be written fn x = x + 1 or x = x + 1 ? Two characters, and I was sure the shorter one would be easier for a model to get right. Asking a model which it prefers is useless — models are sycophantic by default and will validate whichever option you hint you like. So I built grammar-lab : the same code-fixing task, run against both syntaxes, scored by whether the output compiles and passes. https://github.com/almide/almide/tree/develop/research/grammar-lab/ https://github.com/almide/almide/tree/develop/research/grammar-lab/ | Model | fn-lambda | paren-lambda | p-value | |---|---|---|---| | claude-sonnet-4-6 | 100% 25/25 | 100% 25/25 | 1.0 | | claude-haiku-4-5 | 86% 26/30 | 86% 26/30 | 1.0 | Sonnet hits a ceiling on both — no signal there. Haiku, the weaker model, is where a real difference would show up if one existed. It didn't: identical pass rates, p=1.0, the flattest possible null result. No evidence of a difference means the extra fn characters were pure noise, so I cut them. Frontier models essentially never get syntax wrong on a language they've never seen before — that's not where the failures live. There's a clean way to find out, borrowed from someone else's evaluation of a different AI-targeted language: hand a model one cheatsheet, zero other context, and have it implement something nobody writes casually — a red-black tree — from scratch. I ran the same setup on Almide: Claude Opus, one CHEATSHEET.md file, two problems a basic calculator and red-black tree insert/delete , five independent runs each, scored by a separate judge model blind to any target answer. All 10 runs eventually finished. Only 2 of the 10 finished clean on the first try. The other 8 all failed at least once — and the failure category was almost perfectly one-sided: Frontier models essentially never get a new language's grammar wrong. What they get wrong is guessing at a library they've never seen. io.read line doesn't return a Result , but the model assumed it did and slapped a unwrap on it. int.parse is the real function; the model invented int.from string because that's what half a dozen other languages call it. Plugging the actual gap was almost insultingly simple — 13 lines added to the cheatsheet under "stdin & parsing" and that class of failure stopped. The second failure mode was different: habits imported wholesale from other languages. OCaml's let ... in and while ... do , x for boolean negation, an invented .to upper method that doesn't exist. This isn't about not knowing Almide's grammar. It's a model averaging across every language it's ever seen and defaulting to the blend. A dedicated detector catches these and returns a diagnostic that says, plainly, "Almide writes this as ." Three failure categories, in order of how they get fixed: In most languages, a compile error is prose aimed at a person. In Almide, the thing reading that error is the same model that wrote the code and is about to write the fix, with no human in the loop. That reframes the error message completely: not documentation, but an API response, and like any API response it can be wrong in ways that actively mislead the caller. One real case: a model wrote let value, symbol = pair — value was meant as a throwaway local name. Almide also ships a stdlib module literally named value . The type checker at the time mishandled this destructuring pattern and misdiagnosed value as undefined, then suggested: python error: undefined variable 'value' hint: Add import value ← wrong The model dutifully added the import and dug itself in deeper. The error didn't just fail to help; it actively lied, and the model trusted it. The fix was to exclude common local-variable names like value and error from the import-suggestion heuristic. Diagnostics that actively steer a model into a worse state go on a running list and get closed out one at a time. The reference point here is Elm, well known for treating compiler errors as a conversation rather than a verdict: https://elm-lang.org/news/compiler-errors-for-humans https://elm-lang.org/news/compiler-errors-for-humans Almide pushes that a step further by swapping the other side of the conversation from a human to a model. A current diagnostic looks like this: php error E001 : type mismatch in fn 'sum digits': expected Int but got Unit -- unit leak.almd:2:25 in fn 'sum digits' here: let abs n = int.abs n hint: Fix the expression type or change the expected type try: // fn body ends with let abs n = ... a statement, returns Unit . // Add abs n as the trailing expression so the fn returns Int: // // let abs n =