Skip to main content
The self-improvement example closes the loop in “improved by itself”: an agent that periodically studies the factory’s own output — session transcripts, tool-call failures, dropped triggers, PR review feedback — and turns what it finds into concrete, evidenced proposals. Its findings target the application or the .auto/ directory itself: a prompt that keeps producing the same review nit, a trigger whose where filter silently drops real events, a preference a human has now stated twice. Because .auto/ is just YAML in the repository, every accepted proposal becomes a normal pull request that GitHub Sync plans and applies — the factory reconfigures itself through the same door humans use.

How it works

  1. A heartbeat spawns a sweep. Every two hours a cron trigger spawns a fresh session (no live-session bookkeeping needed — each sweep is standalone).
  2. The agent reads its own project. Through the read-only auto.sessions.* introspection tools it triages recent sessions, greps transcripts for repeated failures, and inspects which triggers fired and which were dropped. Through brokered GitHub tools it reads recent PRs: review comments, expressed preferences, repeated friction, CI failures.
  3. It diagnoses, bounded. At most three deep-dives per sweep; every finding must cite its evidence (a tool call, an event, a PR comment, a prompt line) and name the concrete surface to change.
  4. It stays stateful without storing state. Each sweep begins by finding the previous sweep’s report with auto.sessions.list and reading its final message — so resolved problems get closures, recurring ones get escalated, and nothing is re-announced.
  5. It reports where the team lives. Actionable findings land in Slack #dev as one short top-level line plus one threaded detail reply. Quiet sweeps post nothing.

The configuration

Two files. The repository mount is deliberately read-onlycontents: read means there is no push credential, so the agent’s writes are limited to Slack messages by construction. The mount also provisions the brokered GitHub tools: a kind: github tool only works for sessions that hold at least one githubApp mount.
.auto/fragments/environments/agent-runtime.yaml
.auto/agents/self-improvement.yaml
The example assumes a Slack connection named slack; the GitHub tool reads through the project’s GitHub App installation.

Walkthrough

A spawning heartbeat, not a delivering one

Both triggers route spawn, which makes this the simplest possible lifecycle: every sweep is a fresh, self-contained session. Compare the research loop, whose heartbeat delivers into a long-lived concurrency: 1 slot — that shape suits an orchestrator holding in-flight state; this one suits a stateless auditor. The heartbeat declares no message:, so the session starts from the agent’s initialPrompt, which renders the tick’s payload — {{heartbeat.scheduledAt}} — to timestamp the sweep. Each heartbeat trigger becomes one cron schedule (0 */2 * * *, timezone: UTC); see cron and webhooks.

The introspection tools are the evidence base

The auto tool exposes a read-only auto.sessions.* introspection family that turns the project’s history into something an agent can study: Large payloads are truncated to a byte budget by default with targeted-read recipes for recovering full content, so sweeping many sessions stays cheap. See auto tools for the full catalog.

Stateful across sweeps, with no state store

The sweep protocol’s first step — find your previous report — is the whole persistence mechanism. The previous session’s four-section report is durable in its transcript; auto.sessions.list scoped to this agent finds it; auto.sessions.conversation reads it. That gives consecutive sweeps memory (closures, escalations, “already reported”) without a database, a file, or any state the agent could corrupt. The same trick powers the research loop’s thread-as-lab-log; here the transcript itself is the log.

Read-only by construction

Three separate mechanisms keep this agent observational:
  • A read-only mount — every githubApp capability is read, so the checkout stages but the session’s git credential cannot push. The mount is what provisions the brokered GitHub tools at all; an agent with no githubApp mount gets no GitHub tool access, whatever its tool list says.
  • A pared GitHub tool listsearch_pull_requests, pull_request_read, actions_list, actions_get are all reads. Without write tools in the list, the brokered proxy will not offer them, whatever the prompt says.
  • Prompt policy for any external MCP tools you attach: read-only sources (logs, metrics, incidents) are in scope, mutations are not.
That layering matters because this agent’s job is to criticize the system it lives in. Its proposals earn trust precisely because it cannot act on them unilaterally.

From findings to PRs: closing the loop

As shipped, the agent proposes and humans dispose. The natural next step is to let accepted findings become pull requests — against application code or against .auto/ itself. Because agents, prompts, and triggers are plain YAML in the repository, a PR that edits .auto/agents/pr-review.yaml gets a Sync plan comment on the PR showing exactly which resources change and how, and applies only after a human merges. The improvement loop stays gated by review even when the subject is the factory’s own configuration. Two ways to wire it:
  • Raise the mount’s capabilities to contents: write and pullRequests: write, add create_pull_request to the GitHub tool list, and extend the prompt: findings the evidence strongly supports become focused PRs, one finding per PR.
  • Keep this agent read-only and pair it with the packaged Shepherd: the sweep posts a finding to #dev, and a human hands the fix to @auto.shepherd with one mention.

Install from the consolidated agent package

The packaged Self Improvement role is published in @auto/agents:
.auto/agents/self-improvement.yaml
Under GitHub Sync, repoFullName defaults from the Sync binding, so the variables block can be omitted. In the template’s current revision Slack reporting is optional and uses a standard connection named slack; without it, the sweep’s report is the session’s final message, readable in the sessions view. Override inherited fields by declaring them in the importing file, and drop inherited entries with remove: { triggers: [...], tools: [...] }. See managed templates.

Adapt it

  • Replace acme/widgets, slack, and #dev, and pick a cadence that matches your session volume — every 2 hours suits an active project; daily suits a quiet one. Sweeps that mostly find nothing are wasted spend.
  • Tailor “actionable” to your team. A review-heavy team cares about expressed preferences and missing tests; an ops-heavy team cares about incident patterns and flaky tool calls.
  • Attach read-only MCP tools for the data your team actually debugs with — logs, metrics, error trackers — as additional evidence sources (see tools).
  • Keep the evidence-before-verdicts standard and the three-deep-dive bound. An improvement agent that speculates gets muted within a week; one that cites transcripts gets read.

Try it

Merge the PR and let GitHub Sync apply the resources. Rather than waiting two hours, trigger a sweep by mentioning the agent:
Confirm the session reads real evidence, and that the report either names concrete improvements with citations or says plainly why it needs more data. Then let the heartbeat take over.