Jaunt
Reference

Upgrading

Per-version migration notes. Version churn lives only here.

The rest of the reference is versionless. This page collects what changes between releases and what you have to do about it. For the complete log, see the GitHub releases.

Compatibility promise: patch releases (1.3.0 → 1.3.1) do not normally invalidate built modules. The exception is a correctness repair for artifacts whose provenance was previously marked fresh incorrectly; that safety migration is called out explicitly below.

1.7.0

Jaunt 1.7 adds the first alpha TypeScript target. The Python package is now 1.7.0; the project-local worker and marker package use their own prerelease version under @usejaunt/ts.

  • Existing Python projects do not need a migration. Version-1 jaunt.toml files and generated Python artifacts keep their current layout. The release does not intentionally stale an otherwise fresh Python module; upgrade and run jaunt check as usual.

  • A TypeScript target uses version-2 configuration. Install the Python CLI, then add the worker, a supported compiler, and the protected test runtime to the package named by tool_owner:

    npm install -D @usejaunt/ts@next 'typescript@^5.9' vitest fast-check @types/node

    The worker supports Node 20 through 24 and TypeScript 5.8 through 6.x. Start with the TypeScript target guide, then run jaunt sync --language ts before the first model-backed build.

  • Adding TypeScript to a version-1 Python project is mechanical. Preview jaunt migrate --config-v2, review the version-2 target layout, and apply it with jaunt migrate --config-v2 --apply. The migration moves Python-specific settings under [target.py] without changing their loaded values or calling a model.

  • Alpha artifact formats can still change. Preview upgrades with jaunt migrate --language ts. Compatible mirrors, facades, placeholders, sidecars, and fingerprints are repaired deterministically; an incompatible protocol, IR, route, or contract change is reported as model-rebuild rather than guessed.

  • Mixed workspaces operate on both targets by default. Use --language py or --language ts to narrow a command. TypeScript target IDs are qualified as ts:path/to/spec#symbol; existing dotted Python IDs keep their meaning.

1.6.3

  • Removing a magic or generated-test spec now rebuilds its module instead of re-stamping the old body. This prevents deleted implementations and pytest functions from surviving as dead generated text.
  • Generated artifacts last stamped by 1.0.0rc7 through 1.6.2 rebuild once on upgrade because those releases could overwrite the evidence needed to detect an earlier removal-only re-stamp. This is an intentional safety exception to the usual patch-release freshness promise.
  • Context-only rebuilds stay local, while removal and public-API changes still propagate to affected dependents. Pure fingerprint changes and semantically equivalent prose edits retain the free re-stamp path.

1.6.2

  • A single jaunt.toml can now route modules across several packages and mixed flat/src layouts. source_roots and test_roots accept globs; unmatched globs fail during config loading.
  • Generated implementations, stubs, tests, contract batteries, ty checks, and dependency validation follow each module's nearest owning pyproject.toml. Root-level status, check, clean, watch, and daemon jobs cover the full workspace.
  • Run jaunt migrate --merge-projects to prove that existing child projects can be consolidated without changing module names, digests, fingerprints, or artifact paths. Add --apply after reviewing the plan.
  • The semantic gate now defaults to gpt-5.6-luna at medium reasoning. Semantic gate settings remain outside generation fingerprints, so this does not stale generated modules.

1.5.1

Patch release from the sixth adoption-feedback wave, focused on uv workspaces. No generated body changes, and the one restale it causes costs nothing to clear.

  • Modules read stale (fingerprint) after the upgrade — the next build re-stamps them all for free. The build prompt's import rule changed, and the prompt is a fingerprint input, so every built module reports stale once. Their spec sources are unchanged, so jaunt build resolves each through the re-stamp path: it writes the new fingerprint over the existing body with no semantic-gate call and no gpt-5.6-sol call. jaunt check counts fingerprint staleness as drift and exits 4 until you run that one build and commit the re-stamped headers. --json lists the modules under refrozen.
  • Specs that span source roots now stop at a config error instead of building into the wrong package. Under 1.5.0, a multi-root project (source_roots with more than one entry) routed every generated file to the first existing root, which quietly broke packages under the others — status and check read through the same wrong path, so the tree looked fresh while runtime was broken. 1.5.1 makes that a hard config error (exit 2) on build, check, status, and migrate: it fires when governed specs span more than one root, or when they all live under a root that is not the first existing one (the message tells you to reorder). The adopter-verified fix is one jaunt project per package: put a jaunt.toml with source_roots = ["."] at each package's import root and run jaunt from there. Nested default roots (["src", "."]) with specs under src/ resolve by longest path and pass untouched.
  • Generated code may import third-party packages plainly. The build prompt now sanctions the standard library and any installed distribution that the spec module imports or the owning package declares — imported from its real module, not smuggled in through a duck-typed stand-in or a dynamic import. Paired with it, the undeclared-import validator resolves declared dependencies from the pyproject that owns the spec's source file, not just the config-root one. A workspace dependency declared in its own package's pyproject now passes without an entry in build.generated_import_allowlist; see the allowlist.

1.5

Minor release that closes the adoption-feedback backlog: a codex advisories channel, jaunt migrate, an orphaned-artifact gate, and clearer status labels. No built module restales on the upgrade.

  • Nothing restales when you upgrade. The advisories prompt instruction lives in backend code, outside the generation fingerprint, so it cannot invalidate a built module. _STUB_FORMAT_VERSION is unchanged, so committed .pyi stubs stay fresh. Upgrade, run jaunt status, and every module reports fresh.
  • jaunt check now blocks on orphaned artifacts. An orphan is a generated module, .pyi stub, or contract battery whose spec no longer exists. check exits 4 and names the fix. On upgrade this fires only if you already have orphans on disk from a deleted or renamed spec. Clear them once with jaunt clean --orphans (preview with --dry-run), or restore the spec. Hand-authored files are never candidates: only files carrying a jaunt provenance header, plus the .contract.json sidecar next to an orphaned module, can be classified.
  • jaunt migrate retires two upgrade chores. It rewrites legacy raise RuntimeError("spec stub") bodies to ... and re-stamps the header so the change costs no model call, and it re-emits .pyi stubs left stale by a past _STUB_FORMAT_VERSION bump. It plans by default and prints what it would change; pass --apply to write. This closes the 1.4.2 caveat where jaunt check stayed red until you ran a full jaunt build.
  • Advisories are additive. Codex ends its final message with any logical issues it noticed while implementing: a spec ambiguity, a spec that contradicts a dependency's documented API, or a suspected bug in code it read. Fresh builds and test runs print them, --json carries an advisories map, and jaunt log replays them. They never touch digests, staleness, or exit codes.
  • The context_stats skills key is renamed. skills_workspace becomes skills_workspace_seeded to say what it measures: the bytes of skill files seeded on disk and available to the agent, not bytes the agent read. The old skills_workspace key stays as a --json alias for this release.
  • Two new status labels. A module the next build resolves through the free re-stamp path now reads stale (re-stamp: free) instead of stale (structural), and a module-origin spec with no artifact yet is flagged newly governed by module scan before you spend anything.
  • Docs and the jaunt init scaffold lead with ... again. 1.4.2 had switched the example stub body to raise NotImplementedError to satisfy strict type checkers; ... reads plainer, never runs, and carries no runtime risk, so it is the default once more. All stub forms still digest identically. If ty or Pyright flags ... under a concrete return annotation, raise NotImplementedError works just as well and switching between the two never restales a module.

1.4.2

Patch release from the fourth adoption-feedback wave. No built module is invalidated and re-emitting stubs makes no model calls — but note the CI caveat: the stub format version changed, so jaunt check reports committed stubs stale (exit 4) until you run jaunt build once and commit the re-emitted .pyi files.

  • importlib.reload() works on governed modules. Re-executing the same magic_module(__name__) call (same file, same line) is now treated as a reload: the prior registration is replaced instead of raising "already called". Reload-dependent test idioms (monkeypatch.setenv + reload) no longer force a module back to decorator style. A second call at a different line in the same module is still an error.
  • Scaffold and docs lead with raise NotImplementedError. Strict type checkers (ty, Pyright) reject a ... body under a concrete return annotation outside stub files. All stub forms digest identically, so switching your existing ... bodies over is free. (1.5 reverted the scaffold default back to ...; the type-checker note still holds, and either form works.)
  • Emitted .pyi stubs are lint-clean. The unused import jaunt is no longer copied from the spec; names inside string annotations ("Chunker | None") resolve like plain ones (from the generated module's imports, including if TYPE_CHECKING: blocks, with an Any fallback for optional-dependency guards); and when ruff is on PATH the emitted stub is formatted with it. You can drop per-file lint exemptions for jaunt stubs.
  • Refreeze no longer blanks tool_version. Re-stamped generated headers (the free path that resolves digest-scheme migrations and gate-equivalent edits) now record the jaunt version that wrote them, preserving provenance.

1.4.1

Patch release: three post-merge review fixes for magic_module. No built module is invalidated.

  • A handwritten helper reached first no longer runs raw stubs. In 1.4.0, if the first external access to a governed module was a handwritten helper (not a spec name), the helper's calls into sibling specs hit the pre-rebind stubs and silently returned stub results (None). Resolution now fires on the first access to any non-dunder attribute.
  • Circular imports are detected exactly. The mid-execution guard now keys on importlib's __spec__._initializing flag, so a circular importer probing the module — even after the last stub is defined — sees plain stubs instead of triggering eager resolution. The 1.4.0 known-limitation window is closed.
  • A stub shadowed by a later real definition is handwritten. The scan now classifies the last top-level def/class per name, matching Python's last-binding-wins semantics. If you relied on an early stub being governed despite a later real redefinition (unlikely), the real definition now wins.
  • Finalize errors exit structurally. A governed module that fails post-import finalize (for example a spec class with a custom metaclass) now reports as a discovery error (exit 2) instead of an uncaught traceback.

1.4.0

jaunt.magic_module(__name__) — module-level magic — becomes the primary authoring style. Nothing about decorator mode changes; the new call is additive.

  • Converting a decorated spec to magic_module is digest-neutral, as long as the stub body form is unchanged. The @jaunt.magic line never entered the digest, and a module-governed spec renders its signature through the same path a decorated one does, so dropping the decorator does not restale the module.
  • Rewriting raise RuntimeError("spec stub") to ... restales once. That older body is not a recognized stub form — in a governed module it would classify as handwritten, not a spec — so its text is part of the current digest. Standardizing on ... during conversion costs one rebuild per module.
  • Module-level kwargs restale their governed specs. A magic_module(prompt=…) default is part of every governed spec's contract, so adding or editing one restales the file, as any contract change should.
  • Eject, don't hand-strip, in a governed module. Removing a @jaunt.contract or @jaunt.magic marker by hand from a still-stub-shaped function inside a magic_module file silently flips that function to module-magic governance — the scan now picks it up. Use jaunt eject, which leaves committed real code that classifies as handwritten, instead of deleting the marker yourself.

1.3.1

Patch release from the second adoption-feedback wave.

  • Migrating inner @jaunt.magic to @jaunt.sig is free. (Corrected in 1.4.2 — this note originally said to budget one rebuild; adopter reports showed the rename resolves through the re-stamp path with zero model calls. jaunt status may label the module stale until the next jaunt build re-stamps it.)

  • [codex] fingerprint_cli_version now defaults to false. Embedding codex --version in the generation fingerprint coupled jaunt check to environments with the codex binary installed: a CI runner without it restaled a byte-identical tree. If you relied on CLI-version churn triggering rebuilds, set it back to true explicitly. Projects that built with the old default will see a one-time stale (fingerprint) on the next build after upgrading.

  • Fingerprint-only staleness is now labeled. status and check report stale (fingerprint) instead of stale (structural) when specs are byte-identical but the generation fingerprint differs, and print a hint when the cause is a missing codex binary with fingerprint_cli_version = true.

  • .pyi stubs no longer contain from __future__ import annotations. The emitter could place a future import mid-file (a syntax error for ruff and ty). Stale stubs re-emit on the next build; no action needed.

1.3.0

Adoption-feedback release: strict config, .pyi stubs, a CI-grade check, and the @jaunt.sig vocabulary.

  • Generated layout for top-level modules changed. A bare module timing under a source root now generates to __generated__/timing.py instead of the old timing/__generated__/__init__.py, which shadowed the module name. Package members (pkg.modpkg/__generated__/mod.py) are unchanged. If you have a top-level-module project, run jaunt clean && jaunt build once to migrate. See Output Locations.
  • Config validation is strict. Unknown sections and keys are now errors (exit 2) with a did-you-mean suggestion. Upgrading can surface a latent typo that was previously ignored and silently left a setting at its default. Fix the reported key. See Strict validation.
  • emit_stubs defaults on. Builds now write a .pyi beside each spec module, and a missing or stale stub marks the module stale. Set [build] emit_stubs = false to opt out. See .pyi stubs.
  • jaunt check gates magic drift. check now fails on an unbuilt or stale @jaunt.magic module, not just contract drift, so it is the single CI gate for both modes. Scope it with --contracts-only or --magic-only. See the CI guide.
  • @jaunt.sig for sealed methods. In a whole-class spec, mark a method @jaunt.sig to seal its signature (exact params, defaults, annotations, and return type enforced). See writing magic specs.
  • Repo-map drift is decoupled from magic freshness. status and check track treedocs.yaml drift separately; --magic-only skips it entirely.

1.2.0

  • The daemon parks green jobs by default. [daemon] auto_commit now defaults to false: a green job becomes a reviewable proposal you land with jaunt jobs land, rather than an automatic commit. This is the one behavior change existing daemon users feel. To restore the pre-1.2 auto-commit-on-green loop, set auto_commit = true. See the Background Daemon guide.

Next: back to CLI.

On this page