Contract Mode
Pin committed Python or TypeScript behavior with a derived, reviewable test battery.
You have code you want to keep hand-written — an adopted legacy function, a hot path you've tuned, anything the generator should not touch — but you still want its behavior pinned by a contract. That is contract mode. It is how Jaunt pins its own build-critical core: fifteen framework modules run committed Python contract batteries derived this way. TypeScript uses the same lifecycle with exported functions or concrete classes and committed Vitest batteries.
Jaunt picks a mode per symbol from its marker:
- Magic mode: Python uses
@jaunt.magic/@jaunt.test; TypeScript uses private*.jaunt.ts[x]/*.jaunt-test.ts[x]inputs. The behavioral prose is canonical and Jaunt generates the implementation or test battery. - Contract mode: the committed code is canonical. Python uses the runtime
no-op
@jaunt.contractdecorator and derives pytest. TypeScriptadoptadds an@jauntContractTSDoc marker and derives Vitest without moving the source behind a generated facade.
Python contract mode covers top-level functions (sync or async) and whole classes. TypeScript covers exported functions and concrete exported classes.
Write a Python contract
import re
import jaunt
_NON_ALNUM = re.compile(r"[^a-z0-9]+")
@jaunt.contract
def slugify(title: str) -> str:
"""
Convert a human title into a URL-safe slug.
Examples:
- " Hello, World! " -> "hello-world"
- "C++ > Java" -> "c-java"
- "already-slug" -> "already-slug"
Raises:
- "" raises ValueError
- "!!!" raises ValueError
"""
cleaned = _NON_ALNUM.sub("-", title.strip().lower()).strip("-")
if not cleaned:
raise ValueError("title is empty after cleaning")
return cleanedThe Examples: and Raises: prose is what Jaunt derives test cases from
(configurable via [contract] derive = ["examples", "errors"]). The battery is
written to tests/contract/ (configurable via [contract] battery_dir) and is
committed — it's plain pytest that runs with or without Jaunt installed.
Python workflow
jaunt adopt my_app.slug:slugify # add @jaunt.contract to existing code + derive its battery
jaunt reconcile # derive/refresh committed batteries (calls the model)
jaunt check # verify batteries deterministically (CI gate, no model, no API key)
jaunt status # per-function drift state + strength score
jaunt eject my_app.slug:slugify # remove contract tracking; leave plain Python + plain pytest
jaunt eject --allFor Python, jaunt reconcile is the only contract command that can call the model;
adopt derives supported structured cases deterministically. jaunt check is fully
deterministic and offline, which makes it the CI gate: it exits 4 on any blocking
drift state (unbuilt / stale-prose / signature-drift / behavior-drift).
Structured docstring prose (call-form Examples: like the one above) can often
be derived deterministically too, so reconcile doesn't always need the model.
TypeScript workflow
Adopt a committed export by path and symbol:
jaunt adopt src/slug.ts#slugify --language ts
jaunt reconcile --language ts
jaunt check --language ts --contracts-only
jaunt status --language ts
jaunt eject src/slug.ts#slugify --language tsadopt adds one @jauntContract tag to the export's TSDoc and derives its first
battery. The worker projects the declaration without executing the module, and the
battery imports the ordinary committed source. reconcile refreshes stale batteries;
adopt and reconcile can call the test-generation model, while check, status,
and eject do not. check typechecks and runs the committed batteries. eject
removes the marker and its battery while leaving the implementation untouched.
TypeScript batteries live under [target.ts].contract_battery_dir and are ordinary
committed Vitest files. The source declaration and TSDoc are the contract. See the
TypeScript target guide for
mutation strength, package-output checks, and magic-module ejection.
Python async functions and fixtures
Contract mode covers async def functions. The derived test functions are
async def and await the contract function (with pytest-asyncio auto
mode). See examples/contract_async/ in the repo.
Derived cases may declare pytest fixtures in the docstring
(Fixtures: db), resolved from tests/contract/conftest.py. That's the seam
for contracts that need setup — a database handle, a temp dir, a fake clock —
without giving up deterministic derivation.
Whole classes
A class-level Python @jaunt.contract pins the class's public behavior. TypeScript
accepts a concrete exported class and derives cases from its declaration and TSDoc;
abstract classes and authored private or protected members are outside the alpha.
Strength scoring
With [contract] strength = true (the default), jaunt reconcile runs
mutation-based strength scoring: it estimates how well your pinned examples
actually constrain the implementation. A function with one weak example gets a
low score, and jaunt status shows the gap so you know which contracts need more
pinned cases. TypeScript runs supported mutants in disposable overlays and blocks the
transaction when a required mutant survives.
Configuration
[contract]
battery_dir = "tests/contract" # where derived batteries are written
derive = ["examples", "errors"] # case kinds derived from docstring prose
strength = true # mutation-based strength scoring at reconcileVersion-2 workspaces move the language-local battery path under each target. Strength
is shared; Python's derive list continues to select its Python case parsers:
[target.ts]
contract_battery_dir = "tests/contract"
[contract]
strength = trueNext: Background Daemon.