Skip to main content

Trajectory Refinement Testing (TRT) for deterministic agent CI

Project description

Trajectly

CI OpenSSF Scorecard PyPI version

Deterministic regression testing for AI agents, powered by Trajectory Refinement Testing (TRT).

Trajectly records a known-good baseline, replays against deterministic fixtures, and tells you exactly where behavior diverged.

Install

python -m pip install trajectly
python -m trajectly --version

Expected output cue:

trajectly, version X.Y.Z

30-Second Quickstart

Use the standalone procurement demo with pre-recorded fixtures (no API keys required):

git clone https://github.com/trajectly/procurement-approval-demo.git
cd procurement-approval-demo
python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

python -m trajectly run specs/trt-procurement-agent-regression.agent.yaml --project-root .
python -m trajectly report
python -m trajectly repro
python -m trajectly shrink

Expected exits for this intentional regression flow:

  • run ...regression... -> 1 (FAIL)
  • report -> 0
  • repro -> 1 (replays same failing run)
  • shrink -> 0

Observed output excerpts from a fresh run (March 5, 2026):

# run ...regression...
- `trt-procurement-agent`: regression
  - trt: `FAIL` (witness=10)

# report
Source: $PROJECT_ROOT/.trajectly/reports/latest.md

# repro
Repro command: python -m trajectly run "$PROJECT_ROOT/specs/trt-procurement-agent-regression.agent.yaml" --project-root "$PROJECT_ROOT"

# shrink
Shrink completed and report updated with shrink stats.

Artifacts to expect after running the flow:

  • $PROJECT_ROOT/.trajectly/reports/latest.md
  • $PROJECT_ROOT/.trajectly/reports/latest.json
  • $PROJECT_ROOT/.trajectly/repros/<spec>.json
  • $PROJECT_ROOT/.trajectly/repros/<spec>.counterexample.reduced.trace.jsonl

For full step-by-step command/output walkthroughs, use docs/trajectly.md.

What Trajectly Checks

TRT evaluates behavior, not just output text:

  1. Contract compliance: allow/deny rules, sequence constraints, budgets, network, data leak, args.
  2. Refinement: baseline tool-call skeleton must remain a subsequence of the new run.
  3. Witness resolution: report the earliest failing event index (0-based trace index).

Result: stable PASS/FAIL in CI with deterministic repro.

Typical Workflow

python -m trajectly init
python -m trajectly record specs/*.agent.yaml --project-root .
python -m trajectly run specs/*.agent.yaml --project-root .
python -m trajectly report

Typical clean-run cue:

- `my-agent`: clean
  - trt: `PASS`
Latest report: $PROJECT_ROOT/.trajectly/reports/latest.md

Failure triage loop:

python -m trajectly repro
python -m trajectly shrink

Typical failing-run cue:

Repro command: python -m trajectly run "$PROJECT_ROOT/specs/my-agent.agent.yaml" --project-root "$PROJECT_ROOT"
Shrink completed and report updated with shrink stats.

SDK Options

Trajectly supports two instrumentation styles.

Option A: Decorators and adapters

from trajectly.sdk import tool, llm_call

@tool("fetch_ticket")
def fetch_ticket(ticket_id: str) -> dict:
    return {"id": ticket_id, "status": "open"}

@llm_call(provider="openai", model="gpt-4o")
def classify_ticket(text: str) -> str:
    ...

You can also use framework adapters like openai_chat_completion, gemini_generate_content, and langchain_invoke.

Option B: Declarative graph API (trajectly.App)

import trajectly

app = trajectly.App(name="research-agent")

@app.node(id="search_engine", type="tool")
def search(query: str) -> dict:
    ...

@app.node(id="summarizer", type="llm", depends_on=["search_engine"], provider="openai", model="gpt-4o")
def summarize(search_engine: dict) -> str:
    ...

@app.node(id="format_response", type="transform", depends_on=["summarizer"])
def format_response(summarizer: str) -> dict:
    return {"answer": summarizer}

if __name__ == "__main__":
    outputs = app.run(input_data={"query": "Why is the sky blue?"})
    print(outputs["format_response"])

The graph layer uses the same SDKContext instrumentation path and trace event types (tool_called, tool_returned, llm_called, llm_returned, agent_step). No CLI changes are required.

CI Integration

Any CI

python -m pip install trajectly
python -m trajectly run specs/*.agent.yaml --project-root .
python -m trajectly report --pr-comment > comment.md

What to expect:

run exit code:
- 0 when all specs are clean
- 1 when regressions are detected

GitHub Actions

The canonical action is published at trajectly/trajectly-action:

name: Agent Regression Tests
on: [push, pull_request]

jobs:
  trajectly:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: trajectly/trajectly-action@v1
        with:
          spec_glob: "specs/*.agent.yaml"
          project_root: "."

Notes:

  • comment_pr is off by default. Enable it only when you want PR comments and grant pull-requests: write.
  • The in-repo path ./github-action is a temporary compatibility wrapper and will be removed after one release cycle.

See docs/ci_github_actions.md for input options and full behavior.

Architecture at a Glance

Layer Purpose Key modules
core TRT evaluation, traces, fixtures, contracts core/trt, core/contracts.py, core/refinement
cli Command orchestration and reporting cli/commands.py, cli/engine.py
sdk Runtime instrumentation + graph API sdk/context.py, sdk/adapters.py, sdk/graph.py

Documentation

Contributing

For a friendly step-by-step contribution guide, see CONTRIBUTING.md.

git clone https://github.com/trajectly/trajectly.git
cd trajectly
python -m pip install -e ".[dev]"
pytest tests/
ruff check .
mypy src

License

Apache 2.0 -- see LICENSE.

Community

Support Trajectly

If Trajectly is useful to your work, please give the repository a star ⭐: https://github.com/trajectly/trajectly/stargazers

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

trajectly-0.4.2.tar.gz (89.1 kB view details)

Uploaded Source

Built Distribution

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

trajectly-0.4.2-py3-none-any.whl (118.6 kB view details)

Uploaded Python 3

File details

Details for the file trajectly-0.4.2.tar.gz.

File metadata

  • Download URL: trajectly-0.4.2.tar.gz
  • Upload date:
  • Size: 89.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for trajectly-0.4.2.tar.gz
Algorithm Hash digest
SHA256 e9e3854515d7b69fa921b371c35e2812f361b0d6c82f0195d526dad50c0db416
MD5 3fb5810aa247c8f489238adcba135a95
BLAKE2b-256 e0471c6c212e81a8bf7136a15de409da8dc805a80f4e9762a58dd9f74087dde1

See more details on using hashes here.

File details

Details for the file trajectly-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: trajectly-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 118.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for trajectly-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 059fa0f58dd7e124b03a31cd451af65bb024a1ccb311cd34fff65c2e904f74d6
MD5 d609e1318b1fc9a7ce5cbd8b6c1bd222
BLAKE2b-256 250ed3b22341bd514f7cf725724410e0fd2c760a3e5b1a35ae0f99dea6935c28

See more details on using hashes here.

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