Tend allows open-source projects to have an agent as a dutiful junior maintainer. The agent can review PRs, triage issues, fix CI, help out with research, maintain a changelog, sweep the repo for improvements, refine documentation, etc.
Current status: Tend is in its early days. It has been working extremely well in Worktrunk for the past couple of months, such that folks suggested I generalize it into its own project.
To use Tend, a project needs:
- A GitHub account for the agent (for example this project's is @tend-agent)
- One of:
- A Claude Max subscription (harness = "claude")
- A ChatGPT Plus or Pro subscription (experimental), or an OpenAI API key (harness = "codex") — see Codex (experimental alternative).
Tend offers the default code & instructions for the agent. Specifically that means:
- A set of workflow templates
- A very particular set of Skills
- ...skills it has acquired over a very long career (two months)
Each project's agent remains completely under its control, and runs only in the project's Github Actions environment. The Tend project never sees any tokens / keys / etc.
The easiest way to get started is to install the Tend plugin into a local Claude
Code session, and run the /install-tend skill:
claude plugin marketplace add max-sixty/tend
claude plugin install install-tend@tend
claude /install-tendIt'll take 5-15 minutes to set up the config, workflow generation, bot account,
secrets, and branch protection. Tend is configured through a config
file and a repo-local /running-tend skill.
- Tend uses lots of tokens. A Claude subscription, an Anthropic API key,
an OpenAI API key, or a ChatGPT plan is needed to fund the runs.
- Maintainers of sizeable OSS projects get a 20x Claude Max subscription for free from Anthropic.
- A compromise of the runner or credential proxy could expose the bot PAT or
long-lived model credential. The agent cannot read those during normal
operation; subscription-mode Codex receives only an expiring access token.
In the default
maintainermerge mode, the merge restriction prevents a stolen bot credential from landing code.yolodeliberately gives that up for ordinary code while retaining maintainer ownership of Tend's workflows and config, admin-only tags and extra protected branches, and credential gates.
| Workflow | Trigger | What happens |
|---|---|---|
| review | PR opened/updated | Reviews for correctness and duplication. Traces error paths. Monitors CI. Pushes fixes to bot-authored PRs. |
| mention | @bot mention, review | Responds to requests in PR and issue conversations. |
| mention-relay | PR review | Hands review events on same-repo PRs to mention from a job that holds no secrets. |
| triage | Issue opened | Classifies the issue, checks for duplicates, reproduces bugs, attempts conservative fixes. |
| ci-fix | CI fails or is cancelled | Diagnoses the unsuccessful default-branch run, searches for the same pattern elsewhere, and opens a fix PR when needed. |
| nightly | Daily | Resolves conflicts on open PRs, reviews recent commits, surveys ~10 files for bugs and stale docs, closes resolved issues, regenerates tend workflow files. |
| weekly | Weekly | Reviews dependency PRs and approves safe patch and minor updates; merging follows the configured mode. |
| notifications | Every 15 minutes | Drains unread notifications as a recovery queue and repairs conflicts on bot-authored PRs. |
| review-runs | Daily | Reviews recent CI runs for behavioral problems and proposes skill/config improvements. |
| codex-auth-refresh | Weekly | When any workflow uses Codex, renews experimental Plus/Pro auth through its single-writer credential; no-ops for API-key installs. |
The bot reacts 👀 while a session is working: on an issue when it opens, on a PR whenever a review starts, and on a comment that mentions the bot. The reaction comes off when the session ends.
Scheduled workflows also support manual dispatch for testing. GitHub runs
schedule triggers on a best-effort basis and drops ticks under load, so
the intervals above are the requested cadence rather than a guarantee —
observed gaps between runs are routinely longer. All are generated by
default except ci-fix, which requires watched_workflows to be
configured. Any can be omitted on the next regeneration:
workflows:
weekly:
enabled: falseA fork carries these workflows, schedules included. Where the fork has Actions
enabled, each scheduled tick adds a run to its Actions tab. The jobs in those
runs check the repository's owner and skip, so the runs use no runner time and
start no agent. GitHub turns a public fork's scheduled workflows off after 60
days without activity, and the fork's owner can turn them off sooner by
disabling its tend-* workflows (gh workflow disable).
uvx tend@latest init reads .config/tend.yaml and writes tend-*.yaml workflow
files into .github/workflows/. Each workflow handles triggers, skip
conditions, concurrency, and permissions — then calls the composite action
for the configured harness, pinned to the released generator version
(max-sixty/tend/claude@X.Y.Z for Claude, max-sixty/tend/codex@X.Y.Z for Codex).
The nightly regen restamps a newer tag when a new tend version ships.
When the review workflow is generated, init also merges one ignore into
.github/actionlint.yaml: the workflow's concurrency.queue is valid GitHub
syntax that actionlint's schema rejects. The ignore applies only to generated
workflows and preserves the rest of the consumer-owned config.
Both actions run the same security and rate-limit preflight checks and resolve bot identity. They differ in how the agent runs:
- Claude harness — runs the official
claudebinary headless (claude -p) as a non-sudo user inside a hardened systemd unit, behind a local credential-injecting proxy, so the bot token and Anthropic credential never enter the agent's environment. Each workflow's prompt is a slash command (/tend-ci-runner:review) that loads the matching skill. - Codex harness — installs the
@openai/codexCLI, then runscodex execinside the same boundary. GitHub calls use Tend's exact-host proxy. API-key model calls use OpenAI's narrow Responses API proxy; subscription sessions receive only an expiring access token. An AGENTS.md staged into$CODEX_HOMEteaches Codex to resolve/tend-ci-runner:NAMEreferences to the bundled skill markdown.
Edit the config or the generator — not the workflow files. They're regenerated
on every tend@latest init.
Tend gives an agent write access to a repository and points it at input anyone can write: pull requests, issues, comments. The design assumes a session can be hijacked, and bounds what a hijacked session can do. Maintainer mode keeps it from landing code; yolo intentionally permits ordinary code changes while keeping the repository control plane behind a maintainer. Credential isolation, the sandbox, and the environment gate keep the bot and model credentials out of the agent process.
Merge mode determines who can land code on the default branch:
maintainer (default) |
yolo |
|
|---|---|---|
| Bot may merge PRs | No; a maintainer lands them | Yes, for ordinary code; direct pushes stay blocked |
| Changes to workflows, Tend config, CODEOWNERS, or agent instructions | Maintainer lands them | Fresh approval from an independent CODEOWNER |
| Workflow and job overrides | Allowed | Refused; generated jobs retain their audited shape |
Both modes allow runner-side setup:, keep extra protected branches and tags
admin-only, and run security preflight against their respective branch policies
before the agent starts. tend check --fix reconciles the rulesets.
Yolo's runner-side setup support is a trial. A fixed setup command may execute ordinary code the bot merged to the default branch as the runner, before the sandbox starts. That code could reach Tend's job credentials. We may revise this policy as we learn from use. Bot-merged default-branch code can also use generic credentials exposed by other jobs on that branch.
Credential isolation — the bot's GitHub token and the long-lived model credential never enter the agent's process. Tend's proxy on the runner holds the bot token and Claude's model credential, and adds each only to requests bound for its exact host. API-key Codex auth goes through OpenAI's proxy, which forwards only Responses API calls upstream. The agent holds placeholders, except that subscription-mode Codex receives an expiring access-only token, never the rotating refresh token. GitHub authentication applies to any repository the bot account can access, including repositories other than the one that started the run.
Sandbox — both harnesses run the event checkout and the whole agent turn, including any build or test it runs from the event's code, as one process tree inside a hardened systemd unit, under a separate non-sudo user. The system is read-only, all network traffic goes through the credential proxy, and the agent cannot gain privileges, create namespaces, or see other users' processes. The proxy connects to any host but adds credentials only for GitHub and the model API. The agent works in a copy-on-write view of the job's checkout and home, so its writes never reach the job's own files, and the steps after it see the tree that setup left. Its own home sits outside the view, and on a self-hosted runner that home persists between jobs.
Environment-gated credentials — yolo deliberately lets code the bot merges
to the default branch use generic credentials in jobs on that branch. Extra
branches and tags still need bot-inaccessible ref protection or a non-bot
environment reviewer. Tend verifies the exact generated workflows, rejects
other workflows whose environment use is dynamic or hidden behind an external
or ref-qualified reusable workflow, and reserves Tend's operational environment
for the generated jobs. The harness keeps its long-lived credentials out of the
agent process. A bot-controlled workflow cannot reach the bot token or model
auth. A release token or trusted-publishing identity is protected only when
its job stays on a ref the bot cannot update, such as an admin-gated tag. The
bot token and model auth live in the repo's tend GitHub Environment, whose
deployment policy admits only the refs tend check confirmed for Tend's
hardened runtime, so a workflow pushed to any other branch is refused them
before its first step. The credential check accepts the default branch as an
explicit yolo risk. It checks other credential-holding environments — ones
that store a secret or whose jobs request id-token: write — for a non-bot
reviewer or a policy naming verified refs. A trigger the bot both fires and
steers requires a reviewer even when the policy admits yolo's default branch.
It flags any repo-level secret not explicitly
listed in secrets.allowed, where the operational names are refused outright.
tend check --fix creates the environment and sets its policy; moving the
secrets into it stays manual — their values can't be read back.
Config pinning — before the agent starts, both harnesses restore every
CLAUDE.md, CLAUDE.local.md, AGENTS.md, AGENTS.override.md, .claude/,
and .agents/ in the tree, at any depth, from the base branch. Both harnesses
also restore .mcp.json, .claude.json, .gitmodules, .ripgreprc, and
.husky, blocking startup-time code execution and prompt injection from a PR's
own copy of these files.
Immutable releases lock the assets and tag of each release published
after the setting is enabled. The body is not locked — a write-access actor
can still edit an immutable release's notes. tend check requires the setting and
--fix enables it before the next release. Reading the setting takes
repository admin, so a run as the bot checks the newest release's own
immutable flag instead.
Rate limiting — Burst detection (10 PRs and 10 issues per 20 minutes, checked independently) and daily spike detection halt the bot before runaway loops cause damage.
Fixed prompts — Workflow prompts come from the action, not from attacker-controlled input like PR descriptions or comments.
Full threat model: docs/security-model.md.
.config/tend.yaml — only bot_name is required. The default harness runs
Claude; harness: codex selects OpenAI Codex (see
Harnesses below).
bot_name: my-project-bot
# Codex installs pin both values; omit both to use Claude.
# harness: codex
# model: gpt-5.6-sol
# effort: medium # low | medium | high | xhigh; Claude also accepts max
# args: [--max-turns, "40"] # exact additional CLI argumentsTo pause tend, set the TEND_ENABLED repository variable to false:
gh variable set TEND_ENABLED --body falseEvery tend job that runs the agent is then skipped before it starts, with no
regeneration or commit, and gh variable delete TEND_ENABLED resumes them.
Runs already queued or in progress finish; gh run cancel stops one. GitHub
evaluates the check before a job enters the tend environment, so the
variable must be set on the repository or its organization, not in that
environment. Codex's credential refresher keeps running, so a paused
subscription's tokens stay valid. The bot's write access lets it change the
variable too, so the pause is an operating switch rather than a security
control: to cut off a misbehaving bot, revoke its PAT.
The secrets, stored in the repo's tend environment (install-tend creates
it; tend check verifies it), depend on the harness:
| Harness | Required secrets |
|---|---|
claude |
TEND_BOT_TOKEN + one of CLAUDE_CODE_OAUTH_TOKEN (subscription) or ANTHROPIC_API_KEY (API-billed) |
codex |
TEND_BOT_TOKEN + either OPENAI_API_KEY, or the subscription trio CODEX_AUTH_JSON, CODEX_REFRESH_AUTH_JSON, and CODEX_REFRESH_PAT |
TEND_BOT_TOKEN is the bot account's PAT — see
example config for scopes.
CLAUDE_CODE_OAUTH_TOKEN is from claude setup-token. The API keys are
from console.anthropic.com and platform.openai.com. See
Codex (experimental alternative) for the
subscription trio.
docs/security-model.md has the full leak
breakdown.
All other options — setup commands, protected branches, workflow overrides,
schedules — are documented in
docs/tend.example.yaml.
Tend reads project instruction files (AGENTS.md or CLAUDE.md) like any Claude Code session — build commands, test commands, and project conventions go there.
For tend-specific instructions, add a skill overlay at
.claude/skills/running-tend/SKILL.md. Common uses: recording which CI
workflow names tend-ci-fix watches, PR title conventions, label policies.
Tend supports Claude and Codex. Pick whichever fits the credentials and billing path that already work for you; both run the same workflows and skills.
Runs the official claude binary headless (claude -p) in the shared sandbox
boundary behind a local credential-injecting proxy: the bot token and
the Anthropic credential live only in the proxy, never in the agent's
environment. Two auth modes:
CLAUDE_CODE_OAUTH_TOKEN(recommended with a Claude subscription) — Claude Code OAuth token fromclaude setup-token, funded by the subscription's usage limits.ANTHROPIC_API_KEY— standard API key from console.anthropic.com, billed per token against the Console org. Pick this when there's no Claude subscription, when the bot should bill against a dedicated Console org, or when per-key revocation matters.
The proxy injects whichever you set into requests to api.anthropic.com; the agent itself only ever holds a dummy.
Installs @openai/codex and invokes codex exec in the shared sandbox boundary.
GitHub access goes through Tend's exact-host proxy. Under API auth, the OpenAI
key is read from stdin by OpenAI's narrow Responses API proxy and is never
placed in the agent's environment. Under subscription auth, the sandbox gets
an expiring access-only auth.json, never the rotating refresh token. A bundled
AGENTS.md teaches Codex to resolve tend's slash commands to skill markdown.
Codex's own nested sandbox is disabled. One transient systemd unit per run is the single filesystem, network, seccomp, and process-lifetime boundary for both harnesses.
Two auth modes:
- ChatGPT Plus or Pro (experimental): concurrent jobs receive
CODEX_AUTH_JSON, an access-only bundle that Codex cannot refresh. Each repository's serialized weekly workflow holds its ownCODEX_REFRESH_AUTH_JSON, rotates it, then publishes the next access-only bundle using aCODEX_REFRESH_PATscoped to that repository. Do not copy a fullauth.jsonbetween repositories: their refresh jobs would race on the same rotating token. - API:
OPENAI_API_KEYis a standard pay-per-token key from platform.openai.com.
Agent jobs cannot invalidate the refresh state because they receive no refresh
token. An external rotator can temporarily publish access-only auth to several
repositories, but jobs lose authentication after that token expires if the
rotator is offline.
This is experimental because it uses Codex's internal chatgptAuthTokens
mode. The weekly job runs Codex's built-in refresh and persists the updated
full bundle.
Tend pins and tests the Codex version, but an OpenAI change can still break the
weekly refresh until Tend updates.
A badge signals the repo is maintained with tend:
[](https://github.com/max-sixty/tend)The install-tend skill adds this automatically during setup; the kickoff prompt lets you opt out.
MIT
