Skip to main content

rayspec

CI License: Apache-2.0 Python 3.11–3.14 Code of Conduct: Contributor Covenant 2.1

Declarative, YAML-defined workflows for coding agents — running on the Claude Agent SDK and the OpenAI Codex SDK. A pure CLI: no server, no UI, no database.

YAML coordinates. Code computes. Agents judge.

A workflow says what runs, in what order, under which gates, with which agent. Deterministic shell:/python: steps do the computing (and their verdicts are authoritative); prompt: steps hand judgement to an agent; the engine runs the DAG, fans out, loops, pauses for humans, keeps every step's output as a file and resumes from it.

# .rayspec/workflows/fix_issue.yaml
rayspec: 1
name: fix_issue
inputs:
  issue: { type: integer, required: true }
agents:
  triage:      { provider: claude, model: small, access: read-only }
  implementer: { provider: codex,  model: medium, effort: high }
steps:
  - id: fetch
    shell: gh issue view "$RAYSPEC_INPUT_ISSUE" --json title,body
  - id: assess
    needs: [fetch]
    agent: triage
    prompt: "{{ steps.fetch.output }}\nIs this real and worth fixing now?"
    output_schema: { type: object, properties: { verdict: { enum: [fix, skip] } }, required: [verdict] }
  - id: bail
    needs: [assess]
    when: steps.assess.output.verdict == 'skip'
    stop: { status: cancelled, reason: not worth fixing }
  - id: build
    needs: [assess]
    when: steps.assess.output.verdict == 'fix'
    loop:
      max_iterations: 3
      until: steps.check.ok
      steps:
        - id: implement
          agent: implementer
          session: implement
          prompt: "Fix the issue with the smallest change that works. {{ steps.fetch.output }}"
        - id: check
          needs: [implement]
          shell: pytest -q
          allow_failure: true
  - id: confirm
    needs: [build]
    approve: "Open a PR for the fix?"
  - id: pr
    needs: [confirm]
    shell: git push -u origin HEAD && gh pr create --fill
outputs:
  verdict: "{{ steps.assess.output.verdict }}"

What you get: an implicit DAG with parallel siblings, each: fan-out, loop: with an until verdict, when: branches and stop:, approve: gates (TTY prompt, else pause + resume), include: for reusable blocks, named agents, strict Jinja templating with loud failures, Claude and Codex behind one capability-checked adapter, file-based runs with resume, and a git worktree per run by default.

5-minute quickstart

Prerequisites: Python ≥ 3.11, git, and — for real agent runs — a logged-in claude (claude.ai) and/or codex (codex login) on the machine. Dry runs need neither. Both CLIs ship with the SDKs rayspec depends on; rayspec doctor tells you what is missing.

# 1. install from git (a PyPI release is planned):
uv tool install git+https://github.com/rayspec-labs/rayspec-py      # over HTTPS
uv tool install git+ssh://git@github.com/rayspec-labs/rayspec-py    # over SSH
uvx --from git+https://github.com/rayspec-labs/rayspec-py rayspec version   # one-off, nothing installed
uv tool install <path-to-checkout>  # from a local clone (or `uv tool install .` inside it)
rayspec version                     # prints `rayspec <version>`
rayspec doctor                      # Python, RAYSPEC_HOME, git/uv, SDKs, bundled CLIs, auth hints; --probe runs one real turn per provider

# 2. scaffold a project in your repo
rayspec init                        # .rayspec/{workflows/example.yaml, agents/reviewer.yaml, prompts/, config.yaml, stubs/example.yaml}
                                    # + .claude/skills/{rayspec-workflows,rayspec-cli}/ (the coding-agent skills; --no-skill to skip)
                                    # --kind content for a non-code project; --force to overwrite

# 3. check it
rayspec workflows                   # discovered workflows
rayspec validate                    # schema, graph, references, provider capabilities
rayspec plan example                # inputs, resolved agents/models, step order, capability report

# 4. dry-run it — providers become a scripted stub, shell steps are skipped, no login needed
rayspec run example --dry-run --stubs .rayspec/stubs/example.yaml     # ✓ files · ✓ review · outputs table

# 5. run it for real (the example reviews in place; delete its `isolation: none` line to get a git worktree per run)
rayspec run example --input target=src

Prefer to start from a blank file? A workflow is one YAML document; --stubs-init writes the stub answers for a dry run from it:

mkdir -p .rayspec/workflows
cat > .rayspec/workflows/review.yaml <<'EOF'
rayspec: 1
name: review
description: Review the working tree and summarise findings.
inputs:
  target: { type: string, default: "." }
agents:
  reviewer: { provider: claude, model: small, access: read-only,
              instructions: You are a meticulous code reviewer. Be concrete. }
steps:
  - id: files
    shell: git ls-files "{{ inputs.target }}" | head -50
  - id: review
    needs: [files]
    agent: reviewer
    prompt: |
      Review these files and list findings:
      {{ steps.files.output }}
    output_schema:
      type: object
      properties: { verdict: { enum: [approve, request_changes] }, summary: { type: string } }
      required: [verdict, summary]
outputs:
  verdict: "{{ steps.review.output.verdict }}"
  summary: "{{ steps.review.output.summary }}"
EOF

# 3. check it
rayspec workflows                 # discovered workflows
rayspec validate                  # schema, graph, references, provider capabilities
rayspec plan review               # inputs, resolved agents/models, step order, capability report

# 4. dry-run it — providers become a scripted stub, shell steps are skipped, no login needed
rayspec run review --dry-run                            # ✓ files · ✓ review · outputs table
rayspec run review --dry-run --stubs-init stubs.yaml    # optional: scaffold stub answers to edit …
rayspec run review --dry-run --stubs stubs.yaml         # … and replay them

# 5. run it for real (a git worktree on branch rayspec/review-<id> is created for the run)
rayspec run review --input target=src

A run prints one line per step, the outputs: table, the worktree path and branch, tokens/cost and the run directory (~/.rayspec/projects/<slug>/runs/<run-id>/). Exit codes: 0 succeeded · 1 failed · 2 usage/validation error · 3 paused at an approval gate · 4 cancelled · 130 interrupted. Inspect runs with rayspec runs, rayspec show <run> and rayspec logs <run>; continue an interrupted or paused run with rayspec resume <run> (or rayspec run review --resume <run>), decide a gate without a terminal with rayspec approve|reject <run>.

Use rayspec from a coding agent

rayspec ships two Claude Code skills — each a skill file plus compressed references to these docs — that teach an agent to author, validate, dry-run, run and debug workflows without reading this repository: rayspec-workflows for the YAML DSL and rayspec-cli for the command line. Each points at the other. rayspec init writes both into the project; you can also install them by hand:

rayspec skill install             # <project>/.claude/skills/{rayspec-workflows,rayspec-cli}/
rayspec skill install --global    # ~/.claude/skills/…                    (every project)
rayspec skill install rayspec-cli # just one of them

Open a fresh Claude Code session afterwards — the skills load automatically. rayspec skill show tells you whether the installed copies are up to date; rayspec skill install --force refreshes them after upgrading rayspec.

Documentation

  • docs/concepts.md — the mental model (workflow/steps/run, DAG + bodies, scopes, outputs, runs, isolation)
  • docs/schema.md — every field and default, the join truth table, statuses, exit codes, Jinja traps
  • docs/templating.md — context, filters, the shell env-ref rule, python bodies, inputs
  • docs/providers.md — the neutral adapter, the generated capability matrix, Claude/Codex mapping, auth, pricing
  • docs/cli.md — every command, flag and --json shape
  • docs/runs-and-resume.md — the run directory, run.json, events, resume, approval gates
  • docs/policy.mdpolicy.yaml, the worktree change guard, trusted workflows, and what is only advisory
  • docs/isolation.md — worktrees, --repo, registered projects, locks
  • docs/extending.md — adding a provider via entry points, sinks, stores, embedding
  • docs/examples.md — the example projects and what each one shows
  • docs/testing.mdrayspec test: declarative cases, --junit in CI, the golden corpus
  • docs/ci.md — rayspec in CI: the dry-run check as a reusable workflow, and how rayspec is released
  • docs/constitution.md — why the schema is narrow (admissibility test, case law)
  • docs/agent-skill.md — the Claude Code skill: what it contains, rayspec skill install|show|path, how it is generated
  • docs/README.md — index of the above

Module boundaries are documented in CONTRACTS.md.

Status

Releasedrayspec version prints the build you run, and CHANGELOG.md has the history.

Shipped: the schema, loader and validator, templating, the Claude and Codex adapters, the engine (DAG, loop/each/include, approval, resume, dry run, the per-workdir path lock), run-level caps (budget_usd, max_tokens, timeout_total), step-level artifacts:, the file store, worktree isolation and --repo, the Rich live console (rayspec run on a TTY; one line per step otherwise), secret: true inputs, extension entry points for commands, stores, sinks and approval prompts, the packaged Claude Code skill, and the examples/ gallery.

Commands: init, new, doctor, run, resume, approve, reject, cancel, validate, plan, test, explain, eval, show, logs, audit, runs, costs, lock, workflows, agents, providers, plugins, projects, worktrees, trust, schema, skill, completion, version.

Not in this build: a PyPI release — the rayspec name on PyPI currently holds a placeholder, so pip install rayspec does not yet get you this. Install from git (above) until the real release lands.

Development

uv sync --all-groups
uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest -q -m 'not live'
uv run python scripts/gen_capability_matrix.py     # regenerate the matrix in docs/providers.md
uv run python scripts/gen_skill.py                 # regenerate both skills' references/ from docs/ + mirror .claude/skills/ (--check in the gate)

Python ≥ 3.11, anyio-only concurrency.

Contributing

Bug reports, patches and questions are all welcome. CONTRIBUTING.md has the setup, the one-line quality gate, what a test is expected to look like, and the (deliberately high) bar for new schema fields. Everyone taking part is expected to follow the Code of Conduct.

Found something exploitable? Please do not open an issue — SECURITY.md has the private reporting channel, the 90-day disclosure window and the threat model, which is unusual enough to be worth reading first: executing what a workflow declares is the product, so a hostile workflow file is a hostile script, while a leaked secret: true input is a real vulnerability.

License

Apache License 2.0 — see LICENSE and NOTICE.

Contributions are accepted under the Developer Certificate of Origin: sign off each commit with git commit -s, which adds a Signed-off-by: trailer certifying you have the right to submit the work under this license.

Download files

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

Source Distribution

rayspec-1.0.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

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

rayspec-1.0.0-py3-none-any.whl (907.8 kB view details)

Uploaded Python 3

File details

Details for the file rayspec-1.0.0.tar.gz.

File metadata

  • Download URL: rayspec-1.0.0.tar.gz
  • Upload date:
  • Size: 1.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rayspec-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2b45980021e02b1d6e32eb301be314a7429dfe8387fa0a1de497705821be16e4
MD5 c25dfdd92903acd778a152a7706e3da2
BLAKE2b-256 cdcef6ccd270a88ac37e38d7cb33cf1b752c920f92eb0d843e23afd567edf5db

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayspec-1.0.0.tar.gz:

Publisher: release.yml on rayspec-labs/rayspec-py

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

File details

Details for the file rayspec-1.0.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for rayspec-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 597045de2beab25589b59ba50f7625ddbac8c8259a3680dc57426e348d714200
MD5 0555744b839071eb0178fa08d526f814
BLAKE2b-256 e839a0ced43f5026ab78d70ebfc2de22e24f9acf35b6c99a34c4ad0230960b7c

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayspec-1.0.0-py3-none-any.whl:

Publisher: release.yml on rayspec-labs/rayspec-py

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

Release history Release notifications | RSS feed

1.0.2

2 files

1.0.1

2 files

This release

1.0.0 This release

2 files

0.0.1

2 files

Supported by

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