Jaunt
Reference

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 --json mode 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 its example or derived tier. 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.
  • --json is available on build, test, status, check, specs, jobs, sync, design, clean, migrate, watch, instructions, init, log, tree, cache, and skill.

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 sets ok: false and exits 3.
  • 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 legacy skills_workspace alias for this release.
  • emitted_stubs{module: pyi_path}, present only when .pyi stubs were written. stub_warnings appears 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 unrelated advisories key — a list of provenance field names — also appears nested in a stale TypeScript battery's diagnostic data; see below.
  • newly_governed — present only when a magic_module scan 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

CodeMeaning
0Success
2Config, discovery, or dependency-cycle error; bad or wrong-state job id
3Generation error
4Pytest/Vitest failure, contract check/reconcile block, stale tree --check, or a daemon job failed/parked during jobs wait
5Timeout while waiting for daemon jobs

Next: Output Locations.

On this page