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.magicon 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
deforclassunder a top-levelif/trybranch, or nested inside another function, is neither governed nor guaranteed to appear as context. Only direct top-level statements are scanned. magic_modulein__init__.pygoverning 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.