Jaunt
Concepts

How It Works

The mental model: target discovery, DAG builds, prompts, validation, and freshness.

Jaunt has one loop: you write specs, Jaunt generates modules, you review, you tighten the spec, you rebuild. Once you can picture that loop, the rest of the tool follows from it.

The Flow

You write specs
  -> Jaunt discovers them (source_roots / test_roots)
  -> routes them through the Python or TypeScript target
  -> builds a dependency graph
  -> for each module:
       - build prompt = target template + contract + project context
       - Jaunt drives `codex exec` to propose an implementation
       - the target validates the complete candidate
       - Jaunt writes the artifact transaction
  -> Python imports forward from the spec module
  -> TypeScript consumers import the committed facade

Python discovery imports prescreened spec modules and uses Python ASTs, ty, and pytest. TypeScript discovery does not execute application or spec modules: the project-local @usejaunt/ts worker parses private *.jaunt.ts[x] inputs with the owning TypeScript compiler. It validates candidates in an in-memory compiler overlay before any implementation, API mirror, or sidecar is replaced.

What the model sees

At build time Jaunt sends the model a prompt built from a few pieces:

  • A target-specific system template (packaged under src/jaunt/prompts/).
  • Your spec's source segment: the Python signature/docstring/decorator options, or the TypeScript declaration/TSDoc and marker options.
  • Optional per-spec context, such as prompt="...".
  • A repo map rendered from treedocs.yaml, on by default (see Repo Context).

Library and tooling skills are not prompt text. Jaunt seeds them into the Codex workspace under .agents/skills/, where Codex discovers them on its own.

The job the model is handed is narrow: implement the contract the spec implies, nothing more.

The Codex engine

Jaunt has a single generation engine, the OpenAI Codex CLI. For each module Jaunt spawns one codex exec subprocess and hands it the rendered prompt. Python validates the proposed module before writing it. TypeScript composes the proposed internal bindings with its deterministic public boundary, then checks the complete overlay through the owning project and affected project references before writing.

Everything around that step — discovery, staleness, validation, file locations, and runtime integration — belongs to Jaunt, not to Codex. See Codex Engine.

Incremental rebuilds

Jaunt hashes the spec source for each module and writes the digest into the generated file's header. On the next run it skips modules whose digest still matches. Change a spec and only what depends on it regenerates.

The digest covers more than the spec text. Jaunt also fingerprints the generation runtime and the prompt templates, so switching models or editing the preamble can mark files stale too. Change Detection covers exactly what counts as a change and what does not.

Dependency ordering

Jaunt builds a spec-level DAG, so dependencies generate before the specs that use them, and a change to a dependency propagates staleness to its dependents. You declare edges with deps=..., and Jaunt infers the rest best-effort. See Dependencies.

Parallel builds

Jaunt does not build modules in level-synchronous waves. It runs a single ready queue over the dependency graph and keeps every worker busy.

When a build starts, Jaunt takes the stale modules, computes each one's indegree (how many of its dependencies are also stale and still unbuilt), and assigns a priority equal to its critical-path length — the longest chain of dependents that has to wait on it. Modules with no unbuilt dependencies seed a ready heap, ordered longest-critical-path first, so the work that unblocks the most other work goes first.

From there the loop is continuous. Jaunt starts modules off the ready heap until [build] jobs are in flight, then waits for the first one to finish rather than the whole batch. The moment a module completes, every dependent whose last dependency just landed drops onto the ready heap and can start immediately — there is no barrier between "levels." A module that fails marks only its dependents failed (recursively); the rest of the graph keeps building. You get the partial result plus a precise list of what was skipped and why.

Python runtime forwarding

TypeScript does not use runtime forwarding or a loader hook. Production code imports the ordinary facade next to the private spec; normal tsc, bundler, and Node resolution take over from there. See the TypeScript target guide.

A spec module does not generate code when you call it. It forwards your call into the generated module under __generated__/. If that module exists, your call delegates to it. If it does not, you get JauntNotBuiltError and a nudge to run jaunt build. Either way you import the spec module in normal code: the spec module is the stable API surface, and the generated body behind it is free to change on every rebuild.

The @jaunt.magic decorator forwards per symbol — the decorated function or class is a wrapper that resolves the generated object on first call. magic_module forwards per module with a lighter touch. On the first attribute access to any governed spec name, Jaunt imports the generated counterpart, rebinds every spec name in the module to its generated object (functions and classes alike), and then swaps the module back to a plain module. After that first access the module is ordinary: attribute lookups hit the generated objects directly, at zero overhead. It intercepts once, rebinds, and vanishes.

Two-phase registration

magic_module has to register its specs before the stubs below it exist as runtime objects, because the call runs while the module is still executing. It does this in two phases. At call time Jaunt parses the module source, classifies the top-level stubs, and registers each one from the AST alone — signature and docstring are enough to compute digests, so the spec object can be None for now. Once the module finishes importing, a finalize step backfills every entry with its real function or class and runs the same analysis a decorated spec gets. By the time the builder, tester, or jaunt specs looks at the registry, a module-governed spec is indistinguishable from a decorated one — same signature rendering, same digest inputs. Converting a decorated spec to module style is digest-neutral as long as you leave the stub body form alone.

PyPI skills

On jaunt build, Jaunt can generate "skills" from PyPI READMEs for the external libraries you import, best-effort. Those skills, plus Jaunt's bundled builtins, land in the Codex workspace at .agents/skills/ for Codex to read. It tends to improve correctness on unfamiliar libraries without bloating your prompt. See Auto-Generated PyPI Skills.

Where to go deeper

Internals

The design notes behind all of this live in the repository, not on this site. Two are worth reading if you want to know why Jaunt draws the lines where it does. A design-principles doc writes down the rules Jaunt's own generator is held to. And Jaunt is built with itself: the docs/superpowers/ directory holds the written specs and plans behind each Jaunt feature, in the same spec-first style this page describes.

On this page