Skip to main content

git diff for AI agent runs — record golden trajectories, find the first divergent step, gate CI on real regressions

Project description

offtrack

git diff for AI agent runs.

Record golden trajectories. Re-run after any change. See the first step where your agent went off track — and gate CI on it.

CI gated by offtrack Python 3.10+ License: MIT


offtrack demo: record golden trajectories, then catch the model that skips the refund policy check

You bumped a model. Or tweaked a prompt. Or upgraded your agent framework.

Every eval score still looks fine. But your agent now refunds $842 without checking the refund policy — cheaper, faster, and catastrophically wrong:

$ offtrack check

  ✗ refund/over-limit  0/5 aligned
      =  step 0  llm_call     (both runs identical)
      =  step 1  tool_call    lookup_order({"order_id": "TEST-1"})
      ▲ first divergence — expected check_refund_policy, got issue_refund
        baseline  check_refund_policy({"amount_usd": 842.0})
        this run  issue_refund({"order_id": "TEST-1", "amount_usd": 842.0})
      divergence rate rose 0% → 100% (Fisher exact p=0.004, effect +100%)
      Δtokens -46%  Δlatency -30%

  FAIL — exit code 1

Eval scores tell you whether behavior changed. offtrack shows you where: the first divergent step, with cost and latency deltas, from traces stored locally in SQLite. Statistical verdicts (PASS / FAIL / INCONCLUSIVE) gate CI so stochastic LLM variance doesn't cause false alarms — and honest uncertainty doesn't hide behind a green check.

Quickstart

pip install offtrack
offtrack init                 # scaffold offtrack.yaml + baselines/
offtrack record               # capture golden trajectories (N runs per task)
# ...change your model / prompt / framework...
offtrack check                # first divergent step + verdict + exit code

Your agent needs one thing: write capture events (JSONL) to $OFFTRACK_TRACE_DIR, or export OpenTelemetry GenAI traces there — offtrack ingests both semconv generations and the OpenInference flavor.

How it works

  1. Recordofftrack record runs each task N times (default 5) and stores the trajectories: every LLM call, tool call, argument, token count, cost, latency. Baselines auto-export to baselines/*.jsoncommitted to git, so changing golden behavior is a reviewed act in PRs.
  2. Align — new runs are aligned against baselines with Needleman-Wunsch sequence alignment over steps (tool-name gating + structural argument similarity). Reordered parallel calls, retries, and volatile fields (UUIDs, timestamps — masked by default) don't cause false alarms.
  3. Localize — the report points at the first divergent step: missing, extra, or changed — with the argument-level diff and whether the trajectories resynced afterward.
  4. Verdict — baselines are recorded N times so run-to-run variance is measured, not assumed. FAIL requires an exact Fisher test plus a minimum effect size; PASS requires an exact upper confidence bound. Anything else is INCONCLUSIVE with a prescription: "run 2 more repetitions."

CI

- uses: abhi13-tech/offtrack@v0
  with:
    suite: offtrack.yaml
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

FAIL fails the check and posts a PR comment with the divergence table. INCONCLUSIVE warns without blocking (strictness is opt-in via inconclusive-as: fail). Exit codes: 0 pass, 1 fail, 3 inconclusive, 4 setup error — infra problems never masquerade as regressions.

This repo gates itself: every PR runs offtrack against its own demo agent (dogfood.yml), and the gate's failure path is itself under test.

pytest

@pytest.mark.offtrack(task="refund/over-limit")
def test_refund_flow(offtrack):
    with offtrack.record():
        run_my_agent("refund order TEST-1")
    offtrack.assert_matches_baseline()
    offtrack.assert_cost_under(usd=0.05)
    offtrack.assert_max_steps(30)

pytest -n auto (xdist) is fully supported — workers spill trajectories to append-only files; the controller merges at session end.

What offtrack is not

These are complements, not competitors — offtrack is the trajectory-diff layer:

offtrack promptfoo LangSmith EvalView
Unit of comparison step-level trajectory prompt/output pairs hosted traces + evals eval-run snapshots
First-divergence localization
Semantic alignment (retries, reordering, masks) exact-match only
Statistical verdict (variance-aware) pass@k
Local-first, no account SQLite hosted
Vendor-neutral ingest (OTel GenAI, both semconv generations)

offtrack doesn't score answer quality — pair it with an eval framework for that. It catches the thing eval scores structurally miss: procedural regressions in how the agent got there.

Demo

examples/refund-agent/ is a complete offline demo — a small support agent with a written refund policy and two scripted personas: fake-careful (follows policy) and fake-sloppy (a stand-in for a cheaper model that skips the policy check). No API key, no network, $0:

cd examples/refund-agent
AGENT_MODEL=fake-careful offtrack record    # golden: lookup → policy check → escalate
AGENT_MODEL=fake-sloppy  offtrack check     # FAIL: skips policy, refunds $842 directly

The fake mode is a scripted LLM, not a mocked test — the entire real pipeline (capture, ingest, alignment, stats) executes identically in fake and real mode.

Roadmap

  • LangGraph callback + OpenAI/Anthropic SDK capture shims (zero-code instrumentation)
  • Claude Code session ingest
  • Semantic matchers (embedding / LLM-judge) via the Matcher protocol — v1 is deterministic-only by design
  • offtrack bisect — find the commit that introduced a divergence
  • CrewAI / Pydantic-AI adapters · HTML report viewer

Development

git clone https://github.com/abhi13-tech/offtrack && cd offtrack
uv sync --all-extras --group dev
uv run pytest          # 200 tests, all offline
uv run mypy            # strict
just dogfood           # run the gate against the demo locally

MIT licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

offtrack-0.1.0.tar.gz (588.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

offtrack-0.1.0-py3-none-any.whl (57.7 kB view details)

Uploaded Python 3

File details

Details for the file offtrack-0.1.0.tar.gz.

File metadata

  • Download URL: offtrack-0.1.0.tar.gz
  • Upload date:
  • Size: 588.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for offtrack-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8834616da132421fbe9ec64955a67748239c7553fea118cda564cd6217f1d846
MD5 4bdc7fb90c5cebbae9888e4831f63129
BLAKE2b-256 a4df1e8953f4cf106f2f615b09a268818d566211d6d3c7e3278b5d7ef653215b

See more details on using hashes here.

Provenance

The following attestation bundles were made for offtrack-0.1.0.tar.gz:

Publisher: release.yml on abhi13-tech/offtrack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file offtrack-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: offtrack-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 57.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for offtrack-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ebf79eccae355898316fe63b60d4107b0a84abbeafed4346b0438934820dafca
MD5 134de56b3897522b005089e7fc2b64b6
BLAKE2b-256 032a2c0dc7ef7e018e38665e088d840a6bfba331ec531464786c8d13a2e9c180

See more details on using hashes here.

Provenance

The following attestation bundles were made for offtrack-0.1.0-py3-none-any.whl:

Publisher: release.yml on abhi13-tech/offtrack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page