JSON Output
The --json envelope for each command, for agents and CI.
Pass --json to a command and it prints a single JSON object on stdout instead
of human progress. This is the interface for CI annotations, agents, and any
tooling that shells out to Jaunt. The payloads below are trimmed from real runs.
Conventions
- Every envelope carries a
"command"field naming the command and an"ok"boolean. - Errors go to stderr as a human-readable line. In
--jsonmode Jaunt also writes a machine envelope to stdout:{"command": ..., "ok": false, "error": "..."}. So a non-zero exit can still leave parseable JSON on stdout. - Progress bars are suppressed. To keep a progress display while parsing JSON,
use
--progress plain(progress on stderr, JSON on stdout). TypeScript build lines name the active module; TypeScript test lines name the generated battery path and itsexampleorderivedtier. If a test command runs an initial implementation build or a held-out implementation repair, those nested phases keep reporting their active module through the same stderr stream. --jsonis available onbuild,test,status,check,specs,jobs,sync,design,clean,migrate,watch,instructions,init,log,tree,cache, andskill.
Version-1 Python projects keep these envelopes byte-for-byte. Version-2 workspaces
add "schema_version": 2, qualify top-level IDs (py:demo.specs,
ts:src/slug/index), and repeat language-local values under targets.py and
targets.ts. TypeScript errors use a structured {code, message, diagnostics}
record; version-1 errors remain strings.
{
"schema_version": 2,
"command": "build",
"ok": true,
"generated": ["ts:src/slug/index"],
"skipped": ["py:demo.models"],
"refrozen": [],
"failed": {},
"targets": {
"py": {"generated": [], "skipped": ["demo.models"], "refrozen": [], "failed": {}},
"ts": {"generated": ["src/slug/index"], "skipped": [], "refrozen": [], "failed": {}}
}
}build
{
"command": "build",
"ok": true,
"generated": ["demo.specs"],
"skipped": [],
"refrozen": [],
"failed": {},
"cost": {
"api_calls": 1,
"cache_hits": 0,
"prompt_tokens": 79687,
"cached_prompt_tokens": 61952,
"completion_tokens": 557,
"total_tokens": 80244,
"estimated_cost_usd": 0.16383
},
"cache": { "hits": 0, "misses": 1 },
"context_stats": {
"demo.specs": {
"preamble": { "chars": 2790, "est_tokens": 697 },
"module_contract": { "chars": 7, "est_tokens": 1 },
"repo_map": { "chars": 131, "est_tokens": 32 }
}
},
"emitted_stubs": {
"demo.specs": "/path/to/src/demo/specs.pyi"
}
}generated,skipped,refrozen— modules built, left fresh, and re-frozen by the semantic gate without a rebuild.failed—{module: reason}; a non-empty map setsok: falseand exits3.context_stats— per-module char/token estimates for each prompt block (preamble,system,module_contract,deps,package_context,repo_map,blueprint,skills_workspace_seeded). Trimmed above. The seeded-skills block also carries the legacyskills_workspacealias for this release.emitted_stubs—{module: pyi_path}, present only when.pyistubs were written.stub_warningsappears only when a hand-authored stub was skipped.needs_deps— present only when a module inlined logic for an undeclared dependency:{module: [marker, ...]}. Declare those deps to reuse the code.advisories— present only when Codex flagged a logical issue while implementing:{module: [note, ...]}. Advisories never touch digests or exit codes. An unrelatedadvisorieskey — a list of provenance field names — also appears nested in a stale TypeScript battery's diagnosticdata; see below.newly_governed— present only when amagic_modulescan governed a symbol that has no artifact yet:{module: [symbol, ...]}. Those modules build on the next run.
A version-2 TypeScript build adds candidate_outcomes for every module that
entered model generation:
{
"candidate_outcomes": {
"ts:src/detect/index": {
"attempts": 2,
"retry_count": 1,
"retry_reasons": [
"TS2322: optional content_blocks was narrowed to required (src/detect/__generated__/index.ts)"
],
"phase": "committed"
}
}
}attempts counts paid candidate calls, including final transaction repair.
retry_reasons is deduplicated in encounter order. phase is committed or
failed. The top-level cost.api_calls charges every attempt separately, and
the configured build cost ceiling is checked before another retry begins. Mixed
workspaces retain this map at the top level; targets.ts.candidate_outcomes uses
language-local keys such as src/detect/index.
Before a generated implementation lands, compatible committed Jaunt batteries run
against its overlay. If that protected runner is unavailable, the module fails with
JAUNT_TS_COMMITTED_BATTERY_INFRASTRUCTURE without spending another implementation
attempt; independent modules can still complete.
A version-2 TypeScript test run reports one top-level vitest.batteries entry per
example or derived battery. Mixed workspaces repeat it under
targets.ts.vitest.batteries:
{
"vitest": {
"jobs": 6,
"batteries": [
{
"path": "tests/__generated__/detect.example.test.ts",
"tier": "example",
"state": "failed",
"attempts": 2,
"retry_count": 1,
"retry_reasons": [
"JAUNT_TS_TEST_DYNAMIC_LOADER: generated tests must use static imports"
],
"candidate": ".jaunt/typescript/rejected-tests/detect-8a12.candidate.ts",
"candidate_metadata": ".jaunt/typescript/rejected-tests/detect-8a12.json",
"terminal": true
}
]
}
}state can report a fresh/cache/generation phase, verified for a model-free API
transition, or verification-infrastructure when that proof could not run. The last
state uses JAUNT_TS_TEST_VERIFICATION_INFRASTRUCTURE, fails closed, and does not
queue paid generation. Failed generation can add candidate, candidate_metadata,
and terminal; terminal budget exhaustion uses the structured
JAUNT_TS_TEST_GENERATION_EXHAUSTED diagnostic.
An existing stale TypeScript battery carries three views of the same divergence in
the data mapping of its structured diagnostic: mismatches, gating, and optional
advisories. They appear on JAUNT_TS_TEST_BATTERY_STALE and on
JAUNT_TS_TEST_GENERATION_EXHAUSTED when generation exhausted while replacing an
existing stale battery. If initial generation exhausts before the battery file exists,
the exhausted diagnostic carries candidate metadata but none of these divergence
fields. JAUNT_TS_TEST_BATTERY_MISSING also carries none of them.
When the divergence fields are present, mismatches contains every observed
provenance divergence. gating is the subset that failed the check and is never empty,
because an empty set suppresses the diagnostic. advisories is the remainder
(mismatches minus gating) and is present only when non-empty. The rendered
diagnostic message names only the gating fields, so CI consumers should read
gating to learn why jaunt check failed. This battery-diagnostic advisories is a
list of provenance field names nested in a diagnostic's data; the top-level
advisories above is {module: [note, ...]}. The same key name has two shapes at two
nesting levels, so a consumer grepping for advisories will hit both.
The protected full-project overlay validates both live attempts and cache hits.
When the combined overlay still fails, vitest.partial_landing lists the compatible
accepted paths, incompatible rejected paths, unclassified retained paths,
whether the accepted subset was committed, and its isolated runner result. An
accepted path has been proven compatible with the committed baseline; retained
means a bad baseline or runner outage interrupted that proof, so the candidate stays
staged or cached without being committed or evicted. Accepted paths can include
generated batteries and verified reheaders, so one exhausted sibling does not strand
their current stamps.
A rejected battery also carries rejection_reasons. If that battery came from the
response cache, cache_evicted reports whether the rejected bytes were removed.
Eviction compares the stored source before deletion, so it does not remove a newer
replacement. Runner infrastructure failures retain cache entries and commit no
affected candidate. The report can still identify an already proven prefix under
accepted, with the unclassified remainder under retained; the interrupted set
does not land. Independent peers from a separate green group may still land.
The command remains unsuccessful until every requested battery lands.
If the committed battery baseline itself fails, the command reports
JAUNT_TS_TEST_TYPECHECK; vitest.partial_landing.isolation includes
baseline_failure: true and baseline_errors. Unrelated candidates appear under
retained, remain in the response cache, and are neither accepted, rejected, nor
evicted.
The known Codex Selected model is at capacity response is retried twice with
short backoff and does not consume a candidate attempt. A successful retry adds
infrastructure_retries and infrastructure_errors to that battery outcome. If
all three infrastructure calls fail, the battery state is
infrastructure-failed, its diagnostic code is JAUNT_TS_TEST_INFRASTRUCTURE,
and the report still includes every peer battery that completed. This outage consumes
no candidate attempt, writes no rejected-candidate artifact, and does not evict a
cached response.
status
{
"command": "status",
"ok": true,
"stale": ["demo.specs"],
"stale_changes": { "demo.specs": "structural" },
"fresh": [],
"digests": { "demo.specs": "9c39cc26…f656d76a" },
"orphans": [],
"contracts": [],
"contract_review": [],
"tree": { "added": 1, "removed": 0, "restaled": 0 }
}stale_changes classifies why each stale module changed — "structural",
"prose", "fingerprint", or "re-stamp" when the next build resolves the
module through the free re-stamp path (text output renders this value as
re-stamp: free). orphans lists generated artifacts whose spec no longer exists.
tree reports repo-map drift, or is null when the tree is in sync.
A TypeScript status also reports unbuilt and invalid artifacts plus structured diagnostics:
{
"schema_version": 2,
"command": "status",
"ok": true,
"fresh": [],
"stale": ["ts:src/slug/index"],
"stale_changes": {"ts:src/slug/index": "toolchain"},
"unbuilt": ["ts:src/new/index"],
"invalid": {
"ts:src/bad/index": [
{"code": "JAUNT_TS_INVALID", "message": "artifact hash does not match", "severity": "error"}
]
},
"diagnostics": [],
"targets": {
"ts": {
"fresh": [],
"stale": {"src/slug/index": "toolchain"},
"unbuilt": ["src/new/index"],
"invalid": {
"src/bad/index": [
{"code": "JAUNT_TS_INVALID", "message": "artifact hash does not match", "severity": "error"}
]
},
"orphans": [],
"semantic_environment_changes": {
"src/slug/index": {
"before_digest": "same…",
"after_digest": "same…",
"before_records_available": true,
"after_records_available": true,
"added": [],
"removed": [],
"changed": ["tooling:packageManager:package.json"]
}
}
}
}
}semantic_environment_changes appears when the persisted and current
environment identities or tool-only provenance records differ. Tooling records,
such as tooling:packageManager:<path>, are reported here but are excluded from
the model-facing compatibility digest and generation context. Mixed workspaces
place the object under targets.ts. Older sidecars may have only the aggregate
digest; in that case the availability flags and record counts explain why no
exact path diff is possible.
check
{
"command": "check",
"ok": true,
"blocked": [],
"checked": [],
"orphans": [],
"magic": {
"fresh": ["demo.specs"],
"stale": {},
"unbuilt": [],
"orphans": []
}
}blocked/checked cover contract batteries. Orphaned artifacts are split by
kind: contract-battery orphans are listed at the top-level orphans key
(present whenever contracts are in scope), while generated-module, stub, and
sidecar orphans are listed under magic.orphans (present whenever magic is in
scope). A combined jaunt check emits both keys; --contracts-only emits only
the top-level orphans, and --magic-only emits only magic.orphans. Any
blocking drift — including an orphan of either kind — sets ok: false and exits
4. Before the first build the same project reports "unbuilt": ["demo.specs"]
and exits 4.
In a version-2 TypeScript payload, magic is partitioned as magic.ts and includes
invalid; targets.ts.magic repeats the same language-local state. Structured
diagnostics remain available at the top level and in targets.ts.diagnostics.
Version-2 check diagnostics include a deterministic error for every stale,
unbuilt, invalid, or generated-artifact orphan magic blocker; mixed checks do the
same under each language target. The state maps remain authoritative, and
blocked remains contract-only. Standalone version-1 Python payloads retain their
existing shape without a synthesized diagnostics key.
specs
{
"command": "specs",
"ok": true,
"specs": [
{
"ref": "demo.specs:add",
"module": "demo.specs",
"qualname": "add",
"source_file": "/path/to/src/demo/specs.py"
}
],
"dependency_graph": { "demo.specs:add": [] }
}TypeScript specs adds the resolved projects, package owners, and static routes.
The same spec list, routes, and dependency graph are repeated under targets.ts.
test
{
"command": "test",
"ok": true,
"exit_code": 0,
"refrozen": [],
"generation_failed": {}
}For Python, exit_code is pytest's exit code. A TypeScript payload instead carries
the protected runner report under vitest at both the top level and targets.ts.
ok is true only when the target runner passed and no generation failed. A generation
failure exits 3; a pytest or Vitest failure exits 4.
sync
TypeScript sync is model-free. It lists each deterministic boundary artifact it
wrote and repeats the same paths under targets.ts:
{
"schema_version": 2,
"command": "sync",
"ok": true,
"mirrors": ["src/slug/__generated__/index.api.ts"],
"placeholders": ["src/slug/__generated__/index.ts"],
"created_facades": ["src/slug/index.ts"],
"failed": {},
"targets": {
"ts": {
"mirrors": ["src/slug/__generated__/index.api.ts"],
"placeholders": ["src/slug/__generated__/index.ts"],
"created_facades": ["src/slug/index.ts"],
"failed": {}
}
}
}design
design returns a declaration-only patch. The default records but does not apply the
proposal; a later --apply returns the same target with applied: true and makes no
second model call.
{
"schema_version": 2,
"command": "design",
"ok": true,
"target": "ts:src/store/index#TokenStore",
"patch": "*** Begin Patch\n…\n*** End Patch",
"applied": false,
"diagnostics": [],
"usage": {"api_calls": 1},
"targets": {
"ts": {"target": "src/store/index#TokenStore", "applied": false}
}
}jobs
{
"command": "jobs",
"ok": true,
"jobs": [],
"would_rebuild": { "demo.specs": "structural" }
}jobs lists daemon job records; would_rebuild previews the modules the next
build would regenerate and why.
clean
{
"command": "clean",
"ok": true,
"dry_run": true,
"would_remove": [
"/path/to/src/demo/__generated__",
"/path/to/src/demo/specs.pyi"
]
}Without --dry-run, would_remove becomes removed.
migrate
{
"command": "migrate",
"ok": true,
"applied": false,
"actions": [
{
"migration": "legacy-stub-body",
"path": "src/demo/specs.py",
"module": "demo.specs",
"symbol": "parse_email",
"kind": "rewrite-stub-body",
"classification": "re-stamp",
"description": "demo.specs.parse_email: raise RuntimeError('spec stub') -> ... [re-stamp (free)]"
}
]
}applied is false in the default plan-only mode and true under --apply.
Each entry in actions describes one planned or applied change; classification
is re-stamp for an already-governed spec or newly-governs for a legacy body
that would newly govern a symbol. Neither mode calls the model.
For jaunt migrate --language ts, the plan is worker-validated and includes its
input-bound digest and any work that cannot be completed mechanically:
{
"schema_version": 2,
"command": "migrate",
"ok": true,
"language": "ts",
"applied": false,
"plan_digest": "sha256:…",
"blocked": false,
"actions": [
{
"migration": "typescript-artifacts-v1",
"module_id": "ts:src/slug/index",
"path": "src/slug/__generated__/index.api.ts",
"kind": "api-mirror",
"classification": "deterministic-rewrite",
"description": "[deterministic-rewrite] repair API mirror: src/slug/__generated__/index.api.ts"
}
],
"diagnostics": [
{
"code": "JAUNT_TS_MIGRATE_ENVIRONMENT_RECOMPOSE",
"classification": "free-recompose",
"severity": "info",
"module_id": "ts:src/slug/index",
"data": {
"before_digest": "old…",
"after_digest": "new…",
"before_records_available": false,
"after_records_available": true,
"after_record_count": 7
}
}
],
"requires_rebuild": []
}Possible TypeScript classifications are deterministic-rewrite,
free-restamp, free-recompose, model-rebuild, and manual-intervention.
free-recompose means the existing implementation passed the current worker,
compiler, static policy, API, and consumer validation without a model call. A dry-run with a
manual item still has ok: true because the analysis succeeded; an attempted
apply returns ok: false, writes nothing, and includes an error. Successful
applies also include applied_paths.
instructions
{
"command": "instructions",
"ok": true,
"text": "# Jaunt — agent primer\n…",
"project": {
"root": "/path/to/project",
"paths": { "source_roots": ["src"], "test_roots": ["tests"], "generated_dir": "__generated__" },
"engine": "codex",
"model": "gpt-5.6-sol",
"reasoning_effort": "low",
"semantic_gate": { "enabled": true, "model": "gpt-5.6-luna" },
"repo_map": true,
"freshness": { "total": 1, "fresh": 0, "stale": 1, "stale_modules": ["demo.specs"] }
}
}text is the full primer as one string. project is the live snapshot, or
null when no jaunt.toml exists.
watch
watch emits one envelope per rebuild cycle (a watch envelope) rather than
nested build or test documents.
Exit codes
| Code | Meaning |
|---|---|
0 | Success |
2 | Config, discovery, or dependency-cycle error; bad or wrong-state job id |
3 | Generation error |
4 | Pytest/Vitest failure, contract check/reconcile block, stale tree --check, or a daemon job failed/parked during jobs wait |
5 | Timeout while waiting for daemon jobs |
Next: Output Locations.