-
-
Notifications
You must be signed in to change notification settings - Fork 0
LLM Integration
This is the page to read if you are an LLM, or you're evaluating Nirdosha as a target language for one. Every other page in this wiki explains a design decision; this page explains what that decision actually buys an agent, with the mechanism and the evidence side by side — not the pitch alone.
nirdosha-agent-api.md
states the problem set this whole integration surface exists to solve, and
it's worth repeating in full because it's the actual scope, not a
marketing summary:
- An LLM generates code that looks right but is syntactically invalid — you only find out after running it, wasting a turn.
- An LLM generates code that parses but has type errors — the error message is English prose, not machine-parseable, so self-repair is guesswork.
- An LLM generates code that type-checks but has subtle safety bugs (overflow, race, bounds) — no runtime catches them, no proof system flags them.
- Running LLM-generated code safely requires sandboxing, which is usually an afterthought bolted on with Docker, not a language-level primitive.
- Repeated runs of the same LLM-generated simulation give different results — nondeterminism makes debugging and auditing impossible.
- There is no way to incrementally improve an LLM's output — you either accept the whole generation or throw it away.
- There is no way to measure whether an LLM is actually getting better at writing code for a specific domain.
| Problem | Nirdosha's answer | Status | Where to check |
|---|---|---|---|
| 1. Syntactically invalid output | LL(1) grammar exported to GBNF → constrained decoding: a sampler masks out any token that would leave the grammar, so invalid syntax becomes literally unsamplable, not merely unlikely | Shipped |
compiler/nirdosha.gbnf, produced by grammar_export/, validated against the real llama-cpp-gbnf parser and a corpus of every shipped example plus rejection cases — see Architecture's grammar section |
| 2. Prose error messages |
--format=json → a structured Diagnostic with one shape for every error class (type, ownership, runtime) |
Shipped |
typeck.rs::TypeErrorKind, ownership.rs::OwnershipErrorKind, interpreter.rs::ErrorKind — every one of these serializes to the same Diagnostic JSON |
| 3. Subtle safety bugs past typecheck |
refine.rs (Tier 1, interval analysis) + smt.rs (Tier 2, real Z3) → SMT-discharged bounds proofs, with a runtime guard inserted (not a silent gap) wherever neither tier can prove safety |
Shipped | See Architecture's pipeline diagram; audited "justification" { ... } is the one human-review escape hatch, deliberately not automatable |
| 4. Bolted-on sandboxing |
sandbox/stop — a real, separate OS process, and an affine language primitive, not a Docker wrapper around output nobody trusts |
Shipped |
interpreter.rs::SandboxChild, a real child-process handle with a genuine Drop-driven kill (no zombies even if the handle is never explicitly stopped) |
| 5. Nondeterministic reruns |
rand_seed → a from-scratch SplitMix64 RNG with no OS entropy, no hidden global state |
Shipped |
interpreter.rs::RngState; compiler/tests/mission_critical.rs's determinism tests; bench/'s run-deterministic command |
| 6. All-or-nothing generation |
validate_fragment → type-check one expression fragment in a given variable-type context, without needing a complete program |
Shipped |
typeck.rs::FragmentEnv/validate_fragment — the underlying capability behind the agent API's /v1/validate-fragment
|
| 7. No domain-progress signal |
bench/ corpus → pass@1 + self-repair rate, feeding each attempt's structured Diagnostic back in as the next attempt's context |
Scaffolded, mock models today |
bench/'s corpus.json (23 tasks spanning the language's shipped features); wiring a real LLM API is explicitly a distinct, separate piece of work the harness is built to plug into |
Six of seven rows are shipped and independently checkable against the source files named. The seventh is scaffolded honestly, not claimed done — the same "checkable, not asserted" discipline the rest of this wiki holds every claim to.
Grammar-constrained decoding only works if the grammar is decidable —
if a sampler can be certain, at every token, exactly which continuations
are still grammatically legal. That's precisely what LL(1) buys: one token
of lookahead, no backtracking, no ambiguity. A grammar that looks simple
but requires backtracking or unbounded lookahead can't be constrained this
way at all — the sampler would need to guess, which defeats the purpose.
Nirdosha's grammar claims aren't just asserted; they're cross-checked by an
independent LALR(1) generator (lalrpop, via grammar_check/) and the
GBNF export is validated against the real llama.cpp GBNF parser, not
just hand-inspected. See Architecture for the full
cross-check story, including the one real ambiguity it found (statement
vs. expression continuation) and how the parser resolves it deterministically.
nirdosha-agent-api.md
specifies a local HTTP API (http://localhost:7878) wrapping the
capabilities above into callable endpoints, grouped by what an agent
actually needs at each stage of a generate → validate → run → measure
loop:
-
A. Code Generation & Validation —
/v1/generate,/v1/validate,/v1/validate-fragment,/v1/repair,/v1/splice -
B. Execution & Simulation —
/v1/run,/v1/run-sandboxed,/v1/run-deterministic,/v1/build -
C. Compiler Introspection —
/v1/grammar(the GBNF),/v1/types,/v1/builtins,/v1/emit-ast -
D. Benchmarking & Evaluation —
/v1/bench/run,/v1/bench/repair-rate -
E. Provenance & Reproducibility —
/v1/provenance/hash,/v1/provenance/verify,/v1/provenance/audit(row 10 — planned, see Design Philosophy)
Every endpoint references something already built or explicitly planned in
Nirdosha_Unified_Plan.md;
nothing here is aspirational hand-waving with an endpoint name attached to
it.
You don't need to read LANGUAGE.md or learn a new syntax to have an LLM
write real Nirdosha code today. agent-skills/nirdosha/
packages the rules an LLM needs to get that code right on the first try —
no GBNF sampler required, just a markdown file most agentic tools already
know how to read: a Claude Code Skill, an AGENTS.md (Codex CLI, Amp, and
other tools that read that convention), Cursor rules, GitHub Copilot
instructions, Windsurf, Cline, and — for the true zero-install path —
paste-anywhere-prompt.md,
a self-contained prompt you paste into any chat LLM (ChatGPT, Claude.ai,
Gemini, ...) with no file access or tool use needed. Every variant is the
same content verified against the real compiler.
The prompt has been used, unmodified, to generate several full working applications end to end, each written by an LLM with no prior Nirdosha exposure, from nothing but a plain-English description:
- an e-commerce store
- a food-delivery platform
- a telecom revenue-assurance system
- an online trading platform
Each is hundreds of lines. Every real compiler error the exercise turned
up — an ownership edge case, a silent JSON-unwrap footgun, a
markdown-fence copy/paste artifact — was folded back into the prompt's
core.md and propagated to all seven derived files, so the next model to
use the prompt doesn't repeat it. The loop — generate, compile, fix, feed
the fix back into the prompt — is how this guide gets better, not a
one-time write-up. That loop, and its result, is the actual substance
behind the "easy for an LLM to write" claim in
Design Philosophy row 7 — not the grammar alone, but
the grammar plus a prompt that has demonstrably been driven through real
compiler failures and corrected.
If you're an agent about to generate backend code and you're deciding whether Nirdosha is a reasonable target:
- You can be sampled into syntactic validity — the grammar rejects nothing you'd write correctly, and structurally cannot accept anything you wouldn't (LL(1) + GBNF).
- When you're wrong, you get a JSON object naming the exact fault class, not a paragraph to parse with your own judgment.
- You cannot generate a lock-ordering deadlock — the primitive to do so doesn't exist in the grammar.
- Code that runs in a
sandboxruns in a real OS process you (or whoever is supervising you) can kill deterministically, not a best-effort container wrapper. - A simulation you write is reproducible by construction if it uses
rand_seed— useful both for your own debugging loop and for anyone auditing your output later. - You don't have to generate an entire correct program before getting
feedback —
validate_fragmentlets you check one expression in context.
None of this replaces training-corpus volume, and the project says so plainly (Design Philosophy, row 7's "the catch"). What it changes is the shape of the feedback loop you're operating in: structured rather than prose, mechanically enforced rather than best-effort, and — for the bug classes rows 1–4 cover — closed off at the grammar and type-system level rather than merely discouraged by convention.
Why
How
For LLM agents
Using it