Jaunt
Reference

Limitations / Gotchas

Current behavioral constraints and sharp edges.

These are the sharp edges you should know about.

TypeScript alpha

The TypeScript target does not accept .mts, .cts, JavaScript specs, abstract governed classes, authored private/protected class members, or computed member names. Parameter properties and mixin or implements heritage are also rejected. @jauntPreserve is limited to the concrete implementation of a non-overloaded method or accessor; preserve tags on overload groups are rejected. TypeScript 7 is also rejected until its native compiler exposes a stable programmatic API; install a supported project-local compiler such as typescript@^5.9.

These are explicit diagnostics rather than silently weakened validation. See the TypeScript target guide for the supported module layout and workflow.

Code-Generation Engine

Jaunt uses the OpenAI Codex CLI (codex exec) as its sole code-generation engine. Install the codex CLI and authenticate with codex login before running generation commands.

Jaunt is batteries-included: runtime helpers (rich, watchfiles) and the test stack (pytest, pytest-asyncio, anyio) are core dependencies. There are no optional extras.

Hardcoded __generated__ Dir Name

Runtime forwarding for @jaunt.magic reads JAUNT_GENERATED_DIR (default: __generated__). jaunt build/jaunt test set this env var in-process, but that does not persist across separate shell sessions or app runtimes.

If you use a custom paths.generated_dir, set JAUNT_GENERATED_DIR in the environment where your app runs. Otherwise forwarding will still look under __generated__.

Prompt Overrides

The [prompts] config keys (build_system, build_module, test_system, test_module) are treated as file paths by the backend. If you set them, those files must exist on disk and contain the full prompt text — they're not inline strings.

If you want to tweak prompts, copy the defaults from src/jaunt/prompts/, modify them, and version the prompt files alongside your repo.

Dependency Context Plumbing

The dependency DAG, ordering, and staleness propagation all work correctly. However, the backend does not currently pass rich "here is the generated dependency source" context to dependents. The LLM generating a dependent spec doesn't see what its dependencies actually implemented.

In practice, this means you may need to be more explicit in docstrings (or add prompt= context) when a spec depends on non-trivial behavior from another generated spec. Ordering and digest-based staleness still work — it's only the prompt context that's limited.

Auto-Generated PyPI Skills

The skills system adds extra network calls (to PyPI for README fetches) and extra OpenAI calls (to generate skill docs) during jaunt build. These are best-effort: failures emit a warning and the build continues without the missing skills.

This means build output can vary based on your environment, network connectivity, and what's installed in the active venv. If reproducibility matters, commit the generated .agents/skills/ directory.

Discovery Prescreen and Aliased jaunt

To avoid importing side-effectful modules in large repos, discovery imports only modules that show evidence of jaunt specs. During the directory scan (not the --target fast-path), each candidate file must contain the substring jaunt and then pass an AST check for an import jaunt / from jaunt import … statement or a magic / test / contract / preserve decorator. Files without the textual jaunt are never parsed; files that fail to parse are skipped quietly.

Documented limitation: a module that reaches jaunt only through an alias with no textual jaunt anywhere in the file (for example import jaunt as j; @j.magic(...)) is not discovered by a scan. This is contrived and accepted. Work around it by keeping a plain import jaunt (or a from jaunt import …) somewhere in the file, or by naming the module explicitly with --target, which bypasses the prescreen and imports it unconditionally.

Module Magic (magic_module)

jaunt.magic_module(__name__) governs a module by rebinding each spec name to its generated object on first access. That timing is the source of its sharp edges.

  • Import-time capture. Module-level code that runs before the rebind — a top-level call of a governed spec, a class that subclasses one, an assignment that instantiates one — captures the pre-rebind stub and keeps it. Rebinding cannot fix a reference that was already stored (a base class, a dataclass field default, a registry entry, a closure). Unlike decorator mode, a pre-rebind stub class is silently constructible, since its __init__ is just a docstring, so the failure can be quiet. The scan emits a build-time warning when it sees the trap; the rule is that module-level consumption of a spec belongs inside a function. The escape hatch for a base class is an explicit @jaunt.magic on it.
  • Pickle. An instance built from a pre-rebind stub class does not round-trip through pickle: the module attribute now resolves to the generated class, and the two identities disagree.
  • Circular imports that reach into a half-executed governed module bypass resolution the same way they bypass everything else in Python. The names they see are the pre-rebind stubs.
  • Conditional and nested defs are out of scope. A def or class under a top-level if/try branch, or nested inside another function, is neither governed nor guaranteed to appear as context. Only direct top-level statements are scanned.
  • magic_module in __init__.py governing a whole package is not supported yet; call it in the module that holds the stubs.

Codex Authentication

Jaunt delegates generation to the codex CLI. Authenticate that CLI with codex login, or provide CODEX_API_KEY in the environment that runs Jaunt.

The [llm] config section is retained for compatibility, but under Codex llm.api_key_env is informational and does not configure generation authentication.

Next: Upgrading.

On this page