Autonomous gated software sprints with per-agent least privilege, optional Docker isolation, outcome-grounded learning, bounded GitHub repair, and policy-mediated MCP tools. Works with Claude Code, Aider, Codex, OpenCode, or any LLM CLI.
Project description
joshua-agent
An autonomous release gate for your repo. Agents implement and review in cycles — the gate ✅ deploys on GO, ⚠️ flags on CAUTION, ❌ rolls back on REVERT. Define the team in YAML and walk away.
pip install joshua-agent
| Version | 1.25.0 |
| Tests | 631 passing · Python 3.11 / 3.12 / 3.13 |
| Runners | Claude Code · OpenAI Codex · Aider · OpenCode · any CLI tool |
What's new (v1.18–v1.25)
-
Least privilege + MCP (v1.25): every agent now resolves an auditable capability manifest. Gates are read-only by default, delegated agents inherit their parent's boundary, optional Docker mode supplies strong filesystem/network isolation with read-only Git metadata, and Joshua mediates consented MCP
stdiotools through dual allowlists, policy gates, hard limits, secret-redacted audit, and one bounded evidence round. -
GitHub Autopilot (v1.24): turn an issue or failed Actions run into a persisted, bounded repair job and draft PR. Planning is the default;
--executeexplicitly enables a dedicated worktree, gated agent repair, diff policy, local validation, push, PR creation, check monitoring, and at most five configured attempts. It never merges or deploys. -
Outcome-grounded learning (v1.23): immutable episodes connect agent work to the final gate, metric, deploy, and rollback result. Only trusted episodic memories reach prompts; every recall is scored, attributable, searchable, and reversible.
-
Core reliability (v1.22): one runner registry now drives validation, factory dispatch, preflight,
doctor, and the setup wizard. CLI command collisions are removed, the first command modules have been extracted fromcli.py, and the entire codebase passes full Ruff checks. -
OpenCode runner (v1.21): use
runner.type: opencodefor non-interactive OpenCode sprints, including model/binary overrides, sandboxed OpenCode environment variables, CLI preflight, and setup-wizard support. -
Hardened release gate (v1.17): multi-gate consensus is now most-restrictive-wins (REVERT > CAUTION > GO) instead of last-wins. Gates run in parallel. New
gate_policyknobs:min_gates_for_go,metric_required_for_go,metric_regression_limit. -
Sandbox by default (v1.18): agent subprocesses no longer inherit the host's full env (DB URLs, cloud creds, tokens). Only PATH/HOME/locale + your
LLM_*keys +sandbox_allow_env. Disabling it logs a visible warning. -
Real cost control (v1.19): cost rate is configurable via
assumed_cost_per_mtok(was hardcoded $3/MTok).max_daily_cost_usd— declared since v1.16 but dead — is now enforced: per-day tracking, midnight reset, 80% alert, hard stop. -
Leaner CLI (v1.20+):
joshua --helpkeeps a focused 22-command surface, including the newer memory, Autopilot, permissions, and MCP groups. Advanced commands still work when called directly;JOSHUA_SHOW_ALL=1lists everything. -
Docs & benchmarks: README now leads with the release-gate use case. New
benchmarks/ships a reproducible flaky-tests harness (verifiable without an API key).
See the complete changelog for full details.
Contents
- What is it?
- Supported runners
- Demo
- Current status
- How it works
- GitHub Autopilot
- Metrics & Evaluation
- Quick start
- Docker
- Design choices
- Permissions, isolation & MCP
- Full config reference
- CLI
- Examples
- Use Cases
- Architecture
- Security
- Shell Completion
- Documentation
- Contributing
- License
What is it?
joshua-agent is an autonomous release gate for your repository. Each cycle: work agents (dev, bug-hunter, or any custom role) implement the work. A gate agent reviews the output and issues a verdict:
- GO → deploy hook runs automatically
- CAUTION → sprint continues, findings flagged for review
- REVERT → changes rolled back via git, sprint continues on next cycle
On GO the gate auto-deploys your changes; on REVERT it rolls them back via git. Agents share context, extract lessons, and build a wiki that improves future runs. You define the team in a YAML file and walk away.
One day, teams will stop babysitting AI. Instead of prompting one agent at a time — copy, paste, check, repeat — they'll define a team in a YAML file and walk away. The agents run in cycles: execute tasks, review each other, deploy or roll back, extract lessons, sleep, repeat. You come back to a log of what happened and (hopefully) better output than yesterday. — @jorgevazquez, April 2026
Works with any LLM runner. Claude Code, OpenAI Codex, Aider, OpenCode, or any CLI tool that accepts a prompt. Swap it in the YAML — everything else stays the same. Use different models for different agents: Opus for the gate, Sonnet for work agents, a local model for experiments.
Named after the AI in WarGames that learned the only winning move is to keep playing.
Supported runners
| Runner | Install | YAML config |
|---|---|---|
| Claude Code | npm i -g @anthropic-ai/claude-code |
type: claude |
| OpenAI Codex | npm i -g @openai/codex |
type: codex |
| Aider | pip install aider-chat |
type: aider |
| OpenCode | npm i -g opencode-ai |
type: opencode |
| Custom | any CLI | type: custom + command: 'my-tool --input "{prompt_file}" --dir "{cwd}"' |
Custom command templates support {prompt_file}, {cwd}, and {timeout}. The prompt is
written to a private temporary file; {task} and {prompt} are intentionally unsupported
to avoid shell argument-length limits.
Every built-in executable can be overridden with runner.binary or
JOSHUA_<RUNNER>_BINARY (for example, JOSHUA_OPENCODE_BINARY=/srv/bin/opencode). For a
service-account CLI config, set runner.home; OpenCode also accepts
JOSHUA_OPENCODE_HOME.
Demo
Real execution: 2 cycles — Cycle 1 GO (deploy), Cycle 2 REVERT (rollback). asciinema recording
Current status
Stable
- YAML-defined multi-agent sprints with
workandgatephases GO/CAUTION/REVERTverdict loop with snapshot or hillclimb git strategies- CLI workflow:
joshua run,joshua status,joshua doctor,joshua init,joshua examples,joshua explain,joshua tutorial,joshua evolve,joshua serve, and more - HTTP control plane, process-based runtime, persistence, notifications, and restart recovery
- Safety config with command/path allowlists, protected files, objective metrics, and explicit verdict policy wiring
- Outcome-grounded Learning Engine with immutable episodes, trusted/quarantined memory, provenance, and explainable retrieval
- Persisted GitHub issue/CI Autopilot with plan-only default, isolated worktree, bounded repair, draft PR, and check monitoring
- Per-agent least-privilege manifests, read-only gates, optional strong Docker isolation, and policy-mediated MCP tools
Experimental
- Unattended live deploys on real production infrastructure
- Long-run prompt evolution quality and automatic candidate comparison
- Event-driven and on-demand modes that depend on custom task sources and hooks
- Custom runners and hook chains that execute arbitrary shell commands
Work Skills Gate Skills
+--------------+ +----------+
| Dev | | |
| Bug Hunter |--------->| QA |--> Deploy (or Revert)
| CFO | | Review |
| Any Skill... | +----------+
+--------------+ |
^ |
+---- next cycle -------+
How it works
joshua-agent has three core concepts:
- Skills — a skill is any professional role you can describe in a prompt.
dev,qa,bug-hunter,security,cfo,legal-analyst,compliance,pm,tech-writer, or literally anything else. Built-in skills are just prompt templates. You can define your own withsystem_prompt:in YAML — if you can brief a human, you can brief an agent. - Phases — agents are either
work(execute tasks) orgate(review and judge). Work agents produce output. Gate agents read that output and return a verdict:GO(ship it),CAUTION(ship but flag), orREVERT(roll back). This separation exists because unsupervised AI output is dangerous. The gate is a circuit breaker. - Cycles — agents don't run once. They cycle. Each cycle picks the next task (round-robin), runs all work agents, feeds the output to gate agents, acts on the verdict, extracts lessons, and sleeps. Then does it again. This is how real teams work — continuous improvement, not heroic one-off efforts.
The runner abstraction means joshua-agent doesn't care what LLM you use. Claude Code, OpenAI Codex, Aider, OpenCode, or any CLI tool. Swap it in the YAML and everything else stays the same.
GitHub Autopilot
Autopilot is the issue/CI-to-PR path for unattended maintenance. Its default
command only reads GitHub and saves a local plan; remote and repository writes
require --execute:
joshua autopilot start joshua.yaml --issue 42
joshua autopilot resume joshua.yaml ap_0123456789abcdef0123456789abcdef --execute
# Or repair a completed failed GitHub Actions run directly
joshua autopilot start joshua.yaml --run-id 123456789 --execute --no-wait
Every job has an append-only transition history and a hard attempt/diff budget. The agent works in a dedicated Git worktree; deploys, hooks, task sources, and agent-owned GitHub operations are disabled. Only a GO gate, an accepted diff, and all configured local commands allow a normal push and draft PR. Joshua then monitors checks and can consume another bounded attempt on failure. It never auto-merges. See the complete GitHub Autopilot guide.
Metrics & Evaluation
Each sprint cycle, joshua tracks:
- Cycle number — sequential counter since the sprint started
- Agent durations — wall-clock seconds each agent took to run
- Gate verdict —
GO,CAUTION, orREVERTfor the cycle - Consecutive errors — how many cycles in a row ended in failure or error
- Gate findings — the raw text the gate agent returned, injected into the next cycle
Results are stored in the .joshua/ directory alongside your project:
.joshua/
├── checkpoint.json Current cycle number, last verdict, error counts
├── results.tsv One row per cycle — verdict, duration, confidence, description
├── cycles/ Per-cycle Markdown summaries + raw outputs (for replay)
│ ├── cycle-0001.md Human-readable summary: verdict, cost, gate findings
│ └── cycle-0001.json Raw work-agent outputs (used by `joshua replay`)
├── events/ Structured JSON events per cycle
├── lessons/ One file per cycle — raw lessons extracted from agent output
└── wiki/ Curated knowledge entries built from accumulated lessons
To measure progress across cycles, use the status command:
joshua status .joshua
This shows cycle history, verdict distribution, and per-agent timing. Compare cycle 1 vs cycle N to see whether the gate is issuing fewer REVERTs and agents are completing tasks faster.
To evolve agent prompts using accumulated lessons:
joshua evolve config.yaml
joshua evolve curates raw lessons into wiki entries and can rewrite agent prompts to incorporate what was learned.
Honest note: There is no public benchmark dataset for joshua-agent. What you can track concretely on your own project: GO/REVERT ratio over time, cycle-over-cycle agent duration, and gate finding patterns. Use joshua status to build your own baseline.
Quick start
pip install joshua-agent
Docker
# Run a sprint in Docker
docker run --rm -v $(pwd):/workspace \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
ghcr.io/jorgevazquez-vagojo/joshua-agent \
run sprint.yaml
# Full stack (server + Redis)
docker compose up
See docker-compose.yaml and .env.example for configuration.
Example 1 — Safe-by-default development sprint. Start with a wrapper script you control. Keep build, tests, migrations, and health checks inside that script instead of wiring ad hoc shell directly into the first demo.
# dev-sprint.yaml
project:
name: my-app
path: ~/my-app
deploy: "./deploy.sh" # Start here: keep deploy logic behind one script you own
agents:
dev:
skill: dev
tasks:
- "Review code quality and suggest improvements"
- "Refactor for maintainability"
bug-hunter:
skill: bug-hunter
tasks:
- "Scan for uncaught exceptions and error handling gaps"
qa:
skill: qa
sprint:
cycle_sleep: 600
Use a wrapper for deploys. It keeps the first sprint reproducible and makes it much easier to add tests, health checks, migrations, or rollback hooks without rewriting the YAML.
Example 2 — Executive sprint. No code. No deploy command. Agents analyze documents, audit costs, and check compliance. Same framework, different skills.
# executive.yaml
project:
name: acme-corp
path: ~/acme-corp-docs
agents:
cfo:
skill: cfo
system_prompt: |
You are {agent_name}, CFO for {project_name}.
Analyze financial documents in {project_dir}.
tasks:
- "Audit vendor contracts expiring within 90 days"
- "Analyze monthly burn rate from financial reports"
compliance:
skill: compliance
phase: gate
verdict_format: true
system_prompt: |
You are {agent_name}, Compliance Director.
Review all analysis for regulatory compliance.
sprint:
cycle_sleep: 600
gate_blocking: true
joshua run dev-sprint.yaml # Software sprint
joshua run executive.yaml # Business analysis sprint
Agents work, gate reviews, act on verdict. Repeat. Any domain, any role.
What it looks like
============================================================
CYCLE 1 — 2026-04-05T03:14:00
============================================================
[cfo] (cfo) Task: Audit vendor contracts expiring within 90 days
[cfo] OK (189.3s, 3841 chars)
[compliance] (compliance) Reviewing cycle 1...
[compliance] OK (94.2s, 1102 chars)
VERDICT: GO
CYCLE 1 COMPLETE — verdict=GO
Sleeping 600s before next cycle...
Design choices
Skills, not roles. Every agent is a skill defined in YAML. Built-in skills (dev, qa, bug-hunter, security, perf, pm, tech-writer) are convenient starting points — just prompt templates with sensible defaults. But the real power is custom skills: a CFO that audits costs, a legal analyst that reviews contracts, a compliance officer that checks governance, a COO that maps operational bottlenecks. No deploy command needed. No code required. joshua-agent is not a coding tool that happens to support other things. It's a framework for autonomous professional work that happens to be good at code too.
Two phases: work and gate. Work agents do the job. Gate agents judge it. This is the single most important design decision in the framework. Without a gate, you're just running unsupervised AI and hoping for the best. The gate is a circuit breaker — REVERT means nothing ships. In production, we've seen gate agents catch issues that would have broken deployments, flagged non-compliant analysis, and prevented cascading errors. The two-phase model also means you can scale work agents independently of review capacity.
Hardened gate consensus. A single optimistic gate can no longer override a REVERT from another. When you define multiple gate agents, joshua-agent runs them in parallel (gates are read-only reviewers — safe to concurrent) and takes the most-restrictive verdict (REVERT > CAUTION > GO). gate_policy.min_gates_for_go requires N reviewers to agree before a deploy proceeds. Pair with metric_required_for_go and metric_regression_limit to make the objective metric binding — a regression beyond the limit forces REVERT automatically, rather than leaving it to the gate's judgment. Single-gate setups keep their current behavior.
Continuous cycles, not one-shot. Most agent frameworks run once and stop. joshua-agent cycles. Each cycle picks the next task from a round-robin queue, so a dev agent with 10 tasks will work through all of them across 10 cycles. The Learning Engine records the final gate, metric, deploy, and rollback outcome as an immutable episode. Only evidence-backed memories are promoted into future prompts.
Outcome-grounded learning plus wiki curation. Raw output remains available for wiki synthesis, while operational memory follows a stricter lifecycle: candidate → trusted / quarantined / rejected. Recalled memories carry provenance and an explainable score, and joshua memory evaluate compares observed outcomes with and without retrieval. See the Learning Engine guide.
LLM-agnostic. joshua-agent talks to CLI tools, not APIs. Claude Code, OpenAI Codex, Aider, OpenCode, or any custom command that accepts a prompt and returns text. The runner is a one-method interface: run(prompt, cwd, system_prompt, timeout) -> RunResult. Swap it in YAML, everything else stays the same. This means you can use different models for different agents — Opus for the gate, Sonnet for work agents, a local model for experiments.
Gate blocking. When a gate says REVERT, you probably don't want work agents piling more changes on top. gate_blocking: true freezes work agents on the next cycle. Only agents marked run_when_blocked: true (like bug hunters and security scanners) will run. This prevents compounding failures — the bug hunter fixes what the gate flagged, the gate reviews the fix, and only then does normal work resume.
Cross-agent context. Gate findings from the previous cycle get injected into work agents' prompts via {gate_findings}. The QA agent tells the dev agent what's wrong. The dev agent fixes it next cycle. They talk through the framework — no manual copy-paste, no context loss between runs.
Resource-aware scheduling. Each LLM agent consumes significant memory. Running multiple sprints on the same machine can trigger OOM kills (we learned this the hard way). min_memory_gb checks available RAM before each agent run — if memory is low, joshua-agent waits instead of crashing. agent_stagger adds a fixed delay between agent executions to let the system breathe. Together, they let you safely run multiple sprints on a single server.
Objective metrics. Gate agents are good at qualitative review, but they can't replace a test suite. project.objective_metric defines a shell command that returns a number (lower is better). joshua-agent runs it before and after work agents, injects the delta into the gate prompt, and logs both values to results.tsv. The gate agent now has hard data alongside its qualitative judgment. Think pytest --tb=no -q, a benchmark script, or any command that outputs a number.
Protected files. project.protected_files lists globs that work agents must not modify. The instruction is injected directly into the task prompt: "DO NOT modify: tests/**, eval.py". This prevents agents from "gaming" the metric by editing the evaluation or test harness — the same pattern Karpathy uses in autoresearch where prepare.py is read-only.
Hillclimb git strategy. git_strategy: hillclimb turns git into a hill-climbing checkpoint. Before each cycle, joshua-agent commits the current state. After work agents run and the gate reviews, a REVERT verdict triggers git reset --hard to the checkpoint. A GO verdict keeps the commit. The result: every surviving commit in git history is a verified improvement. Compare with snapshot, which creates branches per cycle — hillclimb is simpler and produces a linear history.
Three trigger modes. sprint.trigger controls when cycles run. continuous (default) runs cycles back-to-back with cycle_sleep between them — good for proactive improvement. event polls task sources (Jira, GitHub) every poll_interval seconds and only runs a cycle when there's real work — no tasks, no LLM calls, no tokens burned. on_demand waits for an external trigger via the API (trigger_cycle()) — useful for CI/CD integration where a deploy or PR event kicks off a review.
Permissions, isolation & MCP
v1.25 adds a capability boundary to every agent invocation. Work agents keep a
writable-workspace default; gate agents resolve to a read-only reviewer profile.
You can narrow writable paths, runner tools, optional environment variables,
network intent, and MCP servers per agent. Delegated agents inherit the parent
manifest, and a gate that changes the workspace forces REVERT regardless of
its textual verdict.
Optional Docker mode is the strong boundary: digest-pinned image, no Linux
capabilities, no-new-privileges, non-root UID/GID, read-only root, resource
limits, controlled network, and read-only Git metadata. Native controls and the
post-run guard remain useful, but they do not contain host filesystem or network
access; use require_strong_isolation: true when that distinction matters.
Joshua also implements the current MCP stdio lifecycle. Servers need explicit
command consent, server and agent tool allowlists, time/output/call bounds, and a
policy decision. Tool metadata/results are untrusted evidence; unattended
sprints get one read-only tool round, while mutations require a separate
operator-approved CLI call. See the complete permissions, Docker, and MCP
guide.
Full config reference
project:
name: my-project
path: ~/my-project # Any folder — code, docs, reports, data
deploy: "bash deploy.sh" # Optional — omit for non-code sprints
health_url: http://localhost:3000/health # Optional
objective_metric: "python scripts/quality_metric.py" # Command that prints a number (lower = better)
protected_files: # Globs agents must NOT modify
- "tests/**"
- "eval.py"
program: | # Optional — shared context for ALL agents
## Objective
Reduce p95 latency below 200ms.
## Constraints
- Do NOT modify database schema
- Only edit files in src/api/
runner:
type: claude # claude | codex | aider | opencode | custom
timeout: 1800 # Max seconds per agent run
model: sonnet # Model override (optional)
binary: null # Optional executable override (or JOSHUA_<RUNNER>_BINARY)
home: null # Optional HOME override for service-account CLI auth/config
sandbox: true # DEFAULT ON — strip secrets from agent subprocess env
sandbox_allow_env: [] # Extra env vars to pass through when sandbox=true (e.g. [DATABASE_URL])
assumed_cost_per_mtok: 3.0 # USD per million tokens for cost estimates (set to match your model)
max_sprint_cost_usd: 0.0 # 0 = unlimited; hard stop when cumulative spend exceeds this
max_daily_cost_usd: 0.0 # 0 = unlimited; hard stop when a single day's spend exceeds this
cost_alert_threshold: 0.80 # Warn at 80% of the cost caps above
isolation:
mode: native # native | docker (only Docker is classified strong)
image: "" # Docker: pin image@sha256:<64 lowercase hex chars>
docker_binary: docker # Host-side Docker executable/path
container_binary: "" # Runner executable inside the image (default: host basename)
require_digest: true
pull: never # never | missing | always
read_only_root: true
memory: 1g
cpus: 1.0
pids_limit: 256
tmpfs_size: 128m
agents:
dev:
name: falken # Custom name (optional)
skill: dev # Built-in or custom skill
max_changes: 5 # Max changes per cycle
run_when_blocked: false # Run even when gate is blocked
tasks:
- "Task 1"
- "Task 2" # Round-robin through list
permissions:
profile: developer # auto | reviewer | developer | unrestricted
filesystem: workspace # none | read | workspace
network: inherit # none is enforced only with Docker isolation
write_paths: ["src/**", "tests/**"] # Empty = full workspace for developer
allowed_tools: null # Runner selectors; null = runner defaults, [] = none
allowed_env: null # Narrows runner.sandbox_allow_env for this agent
mcp_servers: []
mcp_tools: [] # tool or server.tool; empty = server allowlist
mcp_max_calls: 0 # 0 disables MCP for this agent; max 10
allow_mcp_mutations: false
require_strong_isolation: false
qa:
skill: qa # Gate skills auto-detect verdict format
permissions:
profile: reviewer # Gate default: read-only; Claude gets Read/Glob/Grep
cfo:
skill: cfo
system_prompt: | # Any prompt you want
You are {agent_name}, a CFO reviewing {project_name}.
Analyze costs, licensing, and resource usage.
tasks:
- "Audit third-party dependency costs"
sprint:
trigger: continuous # continuous | event | on_demand
poll_interval: 300 # Seconds between polls (event/on_demand modes)
cycle_sleep: 300 # Seconds between cycles
max_cycles: 0 # 0 = infinite
max_hours: 96 # 0 = infinite
digest_every: 12 # Summary report every N cycles
retries: 2 # Retry failed agent runs
revert_sleep: 600 # Longer sleep after REVERT
max_consecutive_errors: 5 # Stop after N errors in a row
gate_blocking: true # REVERT blocks work agents
cross_agent_context: true # Gate findings -> work agents
health_check: true # Check health_url each cycle
recovery_deploy: "bash rollback.sh"
git_strategy: hillclimb # none | snapshot | hillclimb
agent_stagger: 30 # Seconds to wait between agent runs
min_memory_gb: 4 # Wait for free RAM before each agent
gate_policy: # Gate hardening — how multiple gates combine
parallel: true # Run gate agents concurrently (read-only, safe)
min_gates_for_go: 1 # Min gates that must vote GO to deploy (>=1)
metric_required_for_go: false # GO -> CAUTION if no objective_metric configured
metric_regression_limit: 0.0 # GO -> REVERT if (metric_after - metric_before) > limit
mcp:
enabled: false # Opt-in; no MCP process starts while false
protocol_version: "2025-11-25"
servers:
repository:
transport: stdio
command: [repo-mcp, --stdio] # Exact argv; shell/inline code is rejected
consent: false # Review exact command/image/env, then set true
allowed_tools: [search, read_symbol] # Empty exposes no tools
mutating_tools: [] # Subset of allowed_tools; policy-gated
allowed_agents: [dev]
env: [] # Environment variable names, never values
timeout_seconds: 30
max_output_chars: 12000
isolation: process # trusted process | docker
network: inherit # network:none requires Docker
image: "" # Docker MCP image@sha256:<digest>
require_digest: true
preflight:
min_disk_gb: 5 # Check disk before each cycle
min_memory_gb: 4 # Check RAM before each cycle
memory_wait_timeout: 120 # Seconds to wait if memory is low
docker_cleanup: true # Auto-clean Docker on low disk
notifications:
type: telegram # telegram | slack | webhook | none
token: ${TELEGRAM_TOKEN}
chat_id: ${TELEGRAM_CHAT_ID}
tracker:
type: jira # jira | github | filesystem | none
base_url: https://x.atlassian.net
project_key: PROJ
memory:
enabled: true
state_dir: .joshua
learning_engine: true # Outcome-grounded episodic memory
lessons_per_cycle: 3 # Max candidate memories extracted per cycle
promotion_threshold: 2 # GO evidence required for trusted patterns
failure_promotion_threshold: 1 # REVERT evidence required for anti-patterns
max_memories_per_prompt: 3 # Trusted memories injected per agent task
max_memory_prompt_chars: 2000 # Prompt budget for recalled experience
retrieval_min_score: 0.20 # Minimum relevance/evidence score
max_lesson_age_cycles: 50 # Legacy-memory compatibility
autopilot:
base_branch: main
remote: origin
branch_prefix: joshua/autopilot
max_repair_attempts: 2 # Bounded to 1..5
max_files_changed: 20
max_changed_lines: 1200 # Additions + deletions
protected_paths: [] # Extends project.protected_files + secret defaults
allowed_paths: [] # Empty = any non-protected repository path
allow_binary_files: false
draft_pr: true # Autopilot never auto-merges
wait_for_checks_seconds: 900
poll_interval_seconds: 15
validation_commands: # Direct commands only; no pipes/redirections/bash -c
- python -m pytest -q
- ruff check .
validation_timeout_seconds: 1200
repair_agents: [] # Empty = all work-phase agents
require_gate: true
runner:
max_tokens_per_cycle: 50000 # Stop adding work agents if estimated tokens exceed this (0 = off)
Dynamic task sources
Agents can pull tasks from external systems instead of a static YAML list:
agents:
dev:
skill: dev
task_source: github # or: jira | gate
task_source_config:
repo: acme/backend # owner/repo
token: ${GITHUB_TOKEN} # optional — for private repos / higher rate limit
labels: "bug,help wanted" # optional label filter
max_results: 20 # issues to consider per cycle
qa:
skill: qa
task_source: jira
task_source_config:
base_url: https://company.atlassian.net
user: ${JIRA_USER}
token: ${JIRA_TOKEN}
jql: "project = PROJ AND type = Bug AND resolution = Unresolved"
fixer:
skill: dev
task_source: gate # Use top issue from last gate findings as task
| Source | Description |
|---|---|
github |
Open issues from a GitHub repo (filters out PRs, round-robin by cycle) |
jira |
Issues from a Jira JQL query (requires HTTPS) |
gate |
Generates task from last gate verdict's top finding (REVERT/CAUTION → resolves issues) |
Template variables available in agent prompts: {agent_name}, {skill}, {project_name}, {project_dir}, {deploy_command} (from project.deploy), {program} (from top-level program:), {memory}, {wiki}, {gate_findings}, {max_changes}.
Each cycle appends one row to .joshua/results.tsv — a greppable, diffable log that doesn't need the CLI:
cycle verdict duration_s agents confidence metric_before metric_after description
1 GO 284.1 dev,bug-hunter 0.94 12.3 8.1 Fixed SQL injection...
2 REVERT 312.0 dev,qa 0.97 8.1 15.2 Auth middleware broke...
CLI
joshua --help shows only primary commands (run, status, serve, compare, trace, init, doctor, …). Advanced commands (promote, rollback, fleet, schedule, secure, verify-audit, replay, export, diff, distill, evolve, audit, …) are hidden from the listing but still work when called directly. Set JOSHUA_SHOW_ALL=1 to list them all.
Onboarding
joshua tutorial # Simulated sprint walkthrough — no API key needed
joshua getting-started # Guided setup walkthrough
joshua examples # List all built-in example configs with descriptions
joshua examples python-api # Copy a template to current directory
joshua examples python-api --show # Print template contents
joshua init # Interactive setup wizard
joshua init --template minimal # Start from a built-in template
joshua schema > joshua-schema.json # Export JSON Schema for IDE YAML autocomplete
joshua explain config.yaml # Human-readable sprint plan + cost estimate
joshua explain-cycle config.yaml -c 3 # Explain a past gate verdict
joshua doctor config.yaml # Pre-flight checks (Python, runner, git, path, creds)
Permissions and MCP
joshua permissions show config.yaml # Resolve profiles; starts nothing
joshua permissions show config.yaml --agent qa --json
joshua permissions audit config.yaml --limit 100 # Permission + MCP JSONL audit
joshua mcp list config.yaml # Review config; starts nothing
joshua mcp inspect config.yaml repository --yes # Start and inspect live allowlist
joshua mcp call config.yaml repository search \
--agent dev --arguments '{"query":"auth"}' --yes
mcp inspect and mcp call require immediate --yes in addition to the
server's persisted consent: true. Mutations also require
--approve-mutation, agent permission, and a non-DENY policy result. See the
security and MCP guide.
Running
joshua run config.yaml # Run a sprint
joshua run config.yaml -n 10 # Max 10 cycles
joshua run config.yaml -H 96 # Max 96 hours
joshua run config.yaml --dry-run # Validate config only
joshua run config.yaml --agents dev,qa # Run only specific agents
Monitoring
joshua status .joshua # Status dashboard
joshua watch . # Live-refresh dashboard (Ctrl+C to stop)
joshua status .joshua --json # Machine-readable JSON (for CI: | jq .checkpoint.cycle)
joshua logs .joshua # Print last 50 log lines
joshua logs .joshua --follow # Live tail (like tail -f)
Trace Viewer
Every cycle generates .joshua/traces/cycle-N.json — a structured tree of the full execution: orchestrator → agents → tool calls → gate → verdict.
joshua trace show . # ASCII tree of latest cycle
joshua trace show . --cycle 3 # Specific cycle
joshua trace show . --format json # Raw JSON (pipe to jq)
joshua trace show . --format flat # Flat indented list
joshua trace list . # Table: cycle | verdict | duration | agents | tokens
The HTTP server also exposes:
GET /sprints/{id}/trace— trace JSON for latest (or?cycle=N) cycleGET /sprints/{id}/trace/list— list of all available cyclesGET /ui/trace/{sprint_id}/{cycle}— interactive D3.js tree viewer in browser
Analysis & export
joshua replay config.yaml --cycle 7 # Re-run gate on saved cycle output (no work agents)
joshua export .joshua # Sprint report as Markdown (stdout)
joshua export .joshua --format json # Sprint report as JSON
joshua export .joshua --cycles 5 # Last 5 cycles only
joshua diff .joshua --cycle 3 --cycle 7 # Compare two cycles side by side (verdict, confidence, findings diff)
joshua diff .joshua # Compare last two cycles
joshua distill .joshua1 .joshua2 # Consolidate lessons across multiple sprints
Environment comparison
joshua compare dev.yaml pre.yaml pro.yaml # Compare existing results side by side
joshua compare dev.yaml pre.yaml pro.yaml --run # Run one QA cycle first, then compare
joshua compare dev.yaml pre.yaml pro.yaml --run --parallel # Run all envs concurrently
joshua compare dev.yaml pre.yaml pro.yaml -f markdown # GFM table (for reports, Jira, etc.)
joshua compare dev.yaml pre.yaml pro.yaml -f json # JSON output (for CI/dashboards)
joshua compare dev.yaml pre.yaml pro.yaml -o report.md -f markdown # Save to file
joshua compare dev.yaml pre.yaml pro.yaml -f markdown -e client@example.com # Email report
compare reads .joshua/checkpoint.json and .joshua/results.tsv from each config's state directory and renders a side-by-side verdict matrix. The first environment is the baseline — regressions against it are flagged automatically.
Example output (--format table):
Environment comparison — 2026-04-08 14:55
──────────────────────────────────────────────────────────────────────────────
Environment Verdict Cycle Conf Dur(s) vs base Top finding
──────────────────────────────────────────────────────────────────────────────
dev ✓ GO 12 0.92 142.3 = Auth fix deployed OK
pre ⚠ CAUTION 9 0.71 189.1 ▼ worse DB pool size warning
pro ✗ REVERT 7 0.97 — ▼ worse SQL injection in /search
──────────────────────────────────────────────────────────────────────────────
→ REVERT in one or more environments — block promotion
Column reference:
| Column | Description |
|---|---|
Environment |
Config filename (without .yaml) |
Verdict |
Last gate verdict from checkpoint.json |
Cycle |
Cycle number when verdict was issued |
Conf |
Gate confidence score (0–1) |
Dur(s) |
Average cycle duration in seconds |
vs base |
Regression vs first environment (= same · ▲ better · ▼ worse) |
Top finding |
First line of last gate findings |
Summary line logic:
| Condition | Summary |
|---|---|
| All envs GO | → All environments GO — ready to promote |
| Any REVERT | → REVERT in one or more environments — block promotion |
| Any CAUTION, no REVERT | → CAUTION in one or more environments — review before promoting |
Release flow
joshua promote dev.yaml pre.yaml pro.yaml # Promote all envs in sequence if all GO
joshua promote dev.yaml pre.yaml pro.yaml --dry-run # Show what would be deployed
joshua promote dev.yaml pre.yaml pro.yaml --force # Skip gate verification between envs
joshua rollback dev.yaml # Git rollback to last snapshot SHA
joshua rollback dev.yaml --to HEAD~1 # Rollback to specific git ref
joshua rollback dev.yaml --dry-run # Show before/after SHA without rolling back
Learning and memory
joshua memory status . # Episodes, rewards and memory states
joshua memory history . --limit 20 # Immutable outcome history
joshua memory search "authentication token" . # Explainable trusted retrieval
joshua memory explain mem_abcd1234 . # Provenance and usage history
joshua memory forget mem_abcd1234 . # Reject, preserving the audit trail
joshua memory evaluate . # Compare outcomes with/without memory
See Learning Engine for promotion, quarantine, scoring, security, configuration, and HTTP endpoints.
GitHub issue and CI repair
joshua autopilot start config.yaml --issue 42 # Persist plan only
joshua autopilot start config.yaml --run-id 123456 --execute # Repair failed run
joshua autopilot status config.yaml # List jobs
joshua autopilot status config.yaml ap_abcd... --json # Full audit history
joshua autopilot resume config.yaml ap_abcd... --execute # Continue plan/checks
joshua autopilot cancel config.yaml ap_abcd... # Retain branch/PR/evidence
The gh CLI supplies authentication. --execute is always explicit; without
it, start/resume perform no branch, agent, push, or PR mutation. See
GitHub Autopilot
for permissions, state transitions, policy settings, recovery, and threat model.
Skills
joshua skill list # List all built-in skills with descriptions
joshua skill new # Interactive wizard to create a custom skill
Custom skills are saved to ~/.joshua/skills/<name>.yaml and available in any config with skill: <name>.
Automation
joshua fleet fleet.yaml # Run multiple projects from a YAML list
joshua fleet fleet.yaml --dry-run # Preview without running
joshua schedule config.yaml --interval 3600 # Run QA every 1 hour
joshua schedule config.yaml --cron "0 8 * * 1-5" # Print crontab command for 8am Mon-Fri
joshua schedule config.yaml --dry-run # Show next 5 run times
joshua watch-git config.yaml --branch main # Trigger a sprint for each new commit
joshua serve # Start HTTP control plane (default: 127.0.0.1:8100)
joshua serve --cert-file c.pem --key-file k.pem # HTTPS
joshua evolve config.yaml # Run evolution + wiki maintenance
joshua completion bash >> ~/.bashrc # Shell completion (bash/zsh/fish)
Deploy safety:
project.deployruns as a shell command with your user's permissions. Shell metacharacters (;,|,`,$() are rejected by config validation. Use dry-run mode (joshua run config.yaml --dry-run) to validate before running. Never use untrusted YAML configs.
Examples
See examples/ for ready-to-use configs:
Business & governance:
executive-team.yaml— CFO + COO + Compliance Directorlegal-review.yaml— Legal Analyst + Risk Assessor + General Counsel
Software development:
minimal.yaml— 3 agents, zero configfull-team.yaml— Dev, Bug Hunter, Security, Perf, PM, QAwordpress.yaml— WordPress: WCAG, SEO, PHP auditsnextjs.yaml— Next.js: TypeScript, React, API auditspython-api.yaml— FastAPI/Django: testing, security, DB auditsautopilot.yaml— bounded GitHub issue / failed-CI repair into a draft PRsecure-mcp.yaml— digest-pinned Docker permissions plus a consent-first MCP template
Use Cases
Four ready-to-run packs for common scenarios:
Pack 1: Continuous Release Gate
Agents: dev (implement feature or fix), qa (quality gate with GO/CAUTION/REVERT).
Runs your CI-equivalent autonomously — auto-deploys on GO, reverts on REVERT, sleeps and repeats. Drop-in replacement for a human code reviewer on low-risk branches. Example: examples/minimal.yaml.
Pack 2: Legacy Modernization
Agents: dev (modernize code), bug-hunter (find regressions), qa (gate review).
Each cycle improves one area of a legacy codebase. The gate blocks the next cycle if tests break or regressions appear, so changes accumulate safely. Example: examples/python-api.yaml.
Pack 3: Client QA Across Environments (DEV / PRE / PRO)
Ideal for agencies running QA as a service on behalf of a client. Each environment gets its own config file. Runs are point-in-time (one review cycle, not continuous) triggered by your CI pipeline or manually before a release.
Setup — one config per environment:
# dev.yaml
project:
name: client-app-dev
path: ~/client-app
health_url: https://dev.client.com/health
objective_metric: "python scripts/smoke_metric.py"
agents:
researcher:
skill: dev
system_prompt: |
You are a QA analyst reviewing the DEV environment of {project_name}.
Check for functional regressions, broken flows, and performance issues.
tasks:
- "Audit all critical user flows and flag any regressions"
qa:
skill: qa
phase: gate
verdict_format: true
sprint:
trigger: on_demand # Only runs when explicitly triggered — no idle cycles
max_cycles: 1 # One review pass, then stop
gate_blocking: true
Duplicate dev.yaml → pre.yaml → pro.yaml, adjusting project.name, health_url, and path for each environment.
Run QA across all three environments:
# Option A — compare existing results (no new LLM calls)
joshua compare dev.yaml pre.yaml pro.yaml
# Option B — run a fresh QA cycle on each, then compare
joshua compare dev.yaml pre.yaml pro.yaml --run
# Option C — run all three in parallel (faster), then compare
joshua compare dev.yaml pre.yaml pro.yaml --run --parallel
# Export results as a Markdown report for the client
joshua compare dev.yaml pre.yaml pro.yaml -f markdown -o qa-report-$(date +%F).md
Example output:
Environment comparison — 2026-04-08 14:55
──────────────────────────────────────────────────────────────────────────────
Environment Verdict Cycle Conf Dur(s) vs base Top finding
──────────────────────────────────────────────────────────────────────────────
dev ✓ GO 1 0.94 138.2 = All smoke tests pass
pre ⚠ CAUTION 1 0.78 201.4 ▼ worse Slow checkout (3.2s avg)
pro ✓ GO 1 0.91 144.0 = No regressions found
──────────────────────────────────────────────────────────────────────────────
→ CAUTION in one or more environments — review before promoting
Delivering results to the client:
The Markdown report (-f markdown -o report.md) is ready to paste into Confluence, Jira, Notion, or send by email. For automated delivery, pipe the output through your existing notification system or attach the file in CI.
Scheduling (SLA):
Add to your CI pipeline to run QA automatically before each release:
# .github/workflows/qa.yml
- name: Environment QA comparison
run: |
pip install joshua-agent
joshua compare dev.yaml pre.yaml pro.yaml --run --parallel \
-f markdown -o qa-report.md
- name: Upload QA report
uses: actions/upload-artifact@v4
with:
name: qa-report
path: qa-report.md
Or use the official Joshua GitHub Action for a zero-config sprint integration:
# .github/workflows/qa.yml
- uses: jorgevazquez-vagojo/joshua-agent@v1.7.0
with:
config: sprint.yaml
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
Or use joshua fleet with parallel: true if you want full sprint logs per environment in addition to the comparison summary.
Note on functional testing: joshua-agent's QA agents are LLM-based reviewers — they analyze code, logs, and output. For browser-level functional testing (Playwright, Cypress, Selenium), run a reviewed wrapper script as
project.objective_metricand let the gate interpret its numeric result. Shell pipelines are intentionally rejected in config; use, for example,objective_metric: "python scripts/playwright_metric.py".
Pack 4: Document & Compliance Review
Agents: analyst (review documents), legal (compliance check), executive (summary + gate).
Multi-agent review cycle for contracts, policies, or technical specs. No deploy command needed — the gate verdict determines whether the document passes or requires revision. Example: examples/legal-review.yaml.
Architecture
joshua/
├── cli.py CLI entry point
├── commands/
│ ├── autopilot.py Autopilot plan/execute/status/resume commands
│ ├── group.py Primary/advanced command visibility policy
│ ├── memory.py Learning Engine CLI (status/search/history/evaluate)
│ ├── security_controls.py Permission and MCP inspection/call commands
│ ├── ui.py Shared spinner and friendly errors
│ └── watch.py Dashboard + git-triggered watch commands
├── autopilot/
│ ├── engine.py Persistent bounded repair state machine
│ ├── github.py Validated GitHub CLI adapter and check normalization
│ ├── models.py Atomic job state and transition history
│ └── workspace.py Dedicated worktrees and change-policy enforcement
├── config.py YAML loader + ${ENV} interpolation
├── sprint.py The loop (work → gate → deploy/revert → learn → sleep → repeat)
├── agents.py Skill definitions + prompt templates
├── security/
│ ├── isolation.py Hardened Docker invocation builder
│ └── permissions.py Per-agent manifests, audit and workspace/Git guards
├── mcp/
│ ├── client.py Bounded MCP stdio lifecycle and tools client
│ └── broker.py Consent, dual allowlists, policy and untrusted evidence
├── runners/
│ ├── base.py LLMRunner interface
│ ├── registry.py Runner catalog, binary resolution, factory
│ ├── claude.py Claude Code
│ ├── codex.py OpenAI Codex
│ ├── aider.py Aider
│ ├── opencode.py OpenCode
│ └── custom.py Any CLI tool
├── memory/
│ ├── engine.py Outcome episodes, rewards, promotion and retrieval
│ ├── lessons.py Extract lessons from each cycle
│ ├── wiki.py Karpa pattern knowledge base
│ └── evolve.py Daily evolution + lint
├── integrations/
│ ├── git.py Snapshot, merge, revert
│ ├── notifications.py Telegram, Slack, webhook
│ └── trackers.py Jira, GitHub Issues, filesystem
└── utils/
├── health.py HTTP health checks
├── preflight.py Disk, memory, Docker cleanup
└── status.py Dashboard
Security
Joshua combines configuration scanning, signed evidence, least privilege, optional containment, and bounded automation:
joshua secure <config>— scan your YAML for hardcoded tokens, passwords, and API keys before committing. Detects Slack tokens, GitHub PATs, and generic secrets. Use--fixto get suggestedexportcommands.- Signed verdicts — set
JOSHUA_SIGNING_KEYto enable HMAC-SHA256 signatures on every row inresults.tsv. Verify integrity withjoshua verify-audit <project_dir>. - Rate limiting — server enforces 30 req/60s per token (configurable via
JOSHUA_RATE_LIMIT). Explicitcheck_rate_limit()function available for per-endpoint use. - Agent manifests — work and gate agents receive phase-aware filesystem, network, tool, environment, and MCP capabilities. Native runs are checked for durable workspace and Git-control changes; a violating gate forces REVERT.
- Strong Docker mode — digest pinning, dropped capabilities, non-root execution, read-only root/Git metadata, resource limits, and network control.
runner.sandboxalone only filters environment variables and is not an OS sandbox. - MCP broker — exact-command consent, stdio-only transport, dual tool allowlists, policy-gated mutations, bounded traffic, untrusted-data delimiters, and a private secret-redacted audit. Process-mode MCP is trusted host code; use Docker for untrusted servers.
- Autopilot boundary — GitHub source text is delimited as untrusted data; repository/ref/job identifiers are validated; work happens in a dedicated worktree; secret paths, external symlinks, binary changes, and diff-budget excesses stop publication.
git/ghnever run through a shell, and no path auto-merges or deploys.
joshua secure my-project.yaml
joshua secure my-project.yaml --fix
JOSHUA_SIGNING_KEY=mysecret joshua run my-project.yaml
joshua verify-audit .joshua/
Shell Completion
Enable tab completion for your shell:
# zsh
echo 'eval "$(_JOSHUA_COMPLETE=zsh_source joshua)"' >> ~/.zshrc
# bash
echo 'eval "$(_JOSHUA_COMPLETE=bash_source joshua)"' >> ~/.bashrc
# fish
echo '_JOSHUA_COMPLETE=fish_source joshua | source' >> ~/.config/fish/config.fish
# Or use the helper command:
joshua completion zsh
Documentation
| Doc | Description |
|---|---|
| Release history | Complete immutable release history from v1.0.0 onward |
| Learning Engine | Outcome rewards, memory lifecycle, retrieval, CLI and HTTP API |
| GitHub Autopilot | Issue/failed-CI lifecycle, safety policy, commands, recovery and threat model |
| Permissions, Docker and MCP | Capability profiles, guarantees, config, MCP lifecycle, audit, migration and threat model |
| E-commerce QA | E-commerce QA skills: researcher, magento-hunter, mobile-tester, ecommerce-qa |
| Primor setup | Production setup guide for running QA sprints against primor.eu |
Contributing
Contributions are welcome — open an issue or a pull request. CI runs on every push to keep main green.
Areas where help is needed:
- Runners: Cursor, Windsurf, VS Code Copilot
- Trackers: Notion, Trello (Linear and Jira/GitHub already supported)
- Notifiers: PagerDuty (Telegram, Slack, Discord, email, webhook already supported)
- Skills: share your custom skill templates
License
MIT. See LICENSE.
Built by Jorge Vazquez. The only winning move is to keep playing.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file joshua_agent-1.25.0.tar.gz.
File metadata
- Download URL: joshua_agent-1.25.0.tar.gz
- Upload date:
- Size: 400.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b66dd37eff31ba272d1f9310da3bf5d24ee1e63c589e1a38d943d87373a52c25
|
|
| MD5 |
04fc94429702ff4382575c8b2cb93a47
|
|
| BLAKE2b-256 |
96aeba5bd7e342dc90ce58775181c3c64baaa57c0c1527d2f03f1ca81e6032f9
|
Provenance
The following attestation bundles were made for joshua_agent-1.25.0.tar.gz:
Publisher:
publish.yml on jorgevazquez-vagojo/joshua-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
joshua_agent-1.25.0.tar.gz -
Subject digest:
b66dd37eff31ba272d1f9310da3bf5d24ee1e63c589e1a38d943d87373a52c25 - Sigstore transparency entry: 2149029073
- Sigstore integration time:
-
Permalink:
jorgevazquez-vagojo/joshua-agent@d077429f77c7a8126a61b0884993146c50e8cca0 -
Branch / Tag:
refs/tags/v1.25.0 - Owner: https://github.com/jorgevazquez-vagojo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d077429f77c7a8126a61b0884993146c50e8cca0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file joshua_agent-1.25.0-py3-none-any.whl.
File metadata
- Download URL: joshua_agent-1.25.0-py3-none-any.whl
- Upload date:
- Size: 265.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8081a2200cc8895df6bfa0794549d44a42612364203c773881ac79a7de8ed465
|
|
| MD5 |
fda3b2bf369062ee71a6c52fa6f70a4e
|
|
| BLAKE2b-256 |
89a2dc2d80e1739068d5e550d8deb10d4680cae74d50d90b7c02a14031e4f3e4
|
Provenance
The following attestation bundles were made for joshua_agent-1.25.0-py3-none-any.whl:
Publisher:
publish.yml on jorgevazquez-vagojo/joshua-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
joshua_agent-1.25.0-py3-none-any.whl -
Subject digest:
8081a2200cc8895df6bfa0794549d44a42612364203c773881ac79a7de8ed465 - Sigstore transparency entry: 2149029103
- Sigstore integration time:
-
Permalink:
jorgevazquez-vagojo/joshua-agent@d077429f77c7a8126a61b0884993146c50e8cca0 -
Branch / Tag:
refs/tags/v1.25.0 - Owner: https://github.com/jorgevazquez-vagojo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d077429f77c7a8126a61b0884993146c50e8cca0 -
Trigger Event:
release
-
Statement type: