Skip to content

LLM Integration

Claude edited this page Sep 7, 2026 · 3 revisions

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.

The seven problems, named honestly

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:

  1. An LLM generates code that looks right but is syntactically invalid — you only find out after running it, wasting a turn.
  2. An LLM generates code that parses but has type errors — the error message is English prose, not machine-parseable, so self-repair is guesswork.
  3. An LLM generates code that type-checks but has subtle safety bugs (overflow, race, bounds) — no runtime catches them, no proof system flags them.
  4. Running LLM-generated code safely requires sandboxing, which is usually an afterthought bolted on with Docker, not a language-level primitive.
  5. Repeated runs of the same LLM-generated simulation give different results — nondeterminism makes debugging and auditing impossible.
  6. There is no way to incrementally improve an LLM's output — you either accept the whole generation or throw it away.
  7. There is no way to measure whether an LLM is actually getting better at writing code for a specific domain.

The mechanism for each, with the evidence it's real

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.

Why constrained decoding is the load-bearing piece

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.

The agent-facing HTTP API

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.

Agent skills — the zero-install path, and the evidence it works

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.

This isn't a claim taken on faith — here's the generated evidence

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.

What this adds up to, concretely

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 sandbox runs 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_fragment lets 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.

Clone this wiki locally