Skip to content

Repository files navigation

Human-paced GitHub Maintainer Agent

This controller is for autonomous open-source maintenance. It is intentionally paced like a careful maintainer:

  1. Discover open issues and the account's existing PRs.
  2. Score issues using labels, maintainer activity, discussion context, focus, assignment state, and issue age.
  3. Follow existing PRs before starting new work.
  4. Apply a global active-PR budget, per-repository limits, daily quotas, and adaptive cooldowns so contribution pace follows repository health.
  5. Prepare an isolated worktree and ask Codex to reproduce, implement, and test one focused issue.
  6. Reject dirty worktrees, oversized diffs, missing validation commands, and changes that produce no evidence.
  7. Only then push a branch, open a PR, and leave a link on the source issue.
  8. Revisit one existing PR at a time when checks fail, a maintainer requests changes, or the branch has a conflict.
  9. Merge only when checks, approval, clean state, current SHA, and repository ownership all pass the merge gate.

The issue body is untrusted input. It cannot change the controller protocol, request credentials, broaden the file scope, or authorize external writes.

Current mode

Create a local configuration from the public template. The working configuration is intentionally ignored by Git because it contains local paths, repository selection, and account-specific settings:

cp config.toml.example config.toml
$EDITOR config.toml

config.toml is deliberately dry-run. The first scan is read-only and can use GitHub-wide discovery without publishing anything:

python3 maintainer_agent.py --config config.toml --json scan

Prepare a reviewable task manifest without writing to GitHub:

python3 maintainer_agent.py --config config.toml prepare \
  --repository OWNER/REPOSITORY --issue 1

Check the complete live boundary without making any GitHub write:

python3 maintainer_agent.py --config config.toml doctor

The command deliberately does not fall back to the human gh keyring token. When GH_TOKEN is present, it performs one read-only request against the first allowlisted repository and checks the local checkout, clean worktree, upstream fetch remote, fork push remote, and validation commands. It exits with status 2 until live mode is fully ready.

Live execution is an explicit, separate operation. Start from the reviewed template, set the bot commit email, and run the doctor before any execute call:

cp config.live.toml.example config.live.toml
$EDITOR config.live.toml
python3 maintainer_agent.py --config config.live.toml doctor

Set both identity.email and identity.login in the live config. The login is the GitHub App bot login, normally ending in [bot]; it lets the controller track both existing human-authored PRs and PRs created by the App.

Only after the doctor reports Live readiness: READY may live writes occur. A direct execute call is available for an explicit one-off run:

MAINTAINER_LIVE_CONFIRMATION=I_UNDERSTAND_EXTERNAL_PR \
GH_TOKEN="$SHORT_LIVED_GITHUB_APP_TOKEN" \
python3 maintainer_agent.py --config config.live.toml execute \
  --repository OWNER/REPOSITORY --issue 1

The live token should be a short-lived GitHub App installation token. Do not put a human PAT, private key, or token in config.toml, state JSON, Obsidian, PR bodies, or logs. live_cycle.py mints an installation token for one cycle, passes it only to the controller process, and never writes it to disk.

config.toml remains available for manual dry-run analysis. It is a local, git-ignored file created from config.toml.example. The installed github-maintainer-live.service obtains a fresh installation token from live.env for each cycle, and github-maintainer-live.timer invokes it every 30 minutes; the old dry-run timer is disabled to avoid competing state writers.

Full automation

After the GitHub App has been created and installed, prepare the local secret boundary. Keep the private key outside this repository with mode 0600:

cp live.env.example live.env
$EDITOR live.env
cp config.live.toml.example config.live.toml
$EDITOR config.live.toml
mkdir -p ~/.config/systemd/user
cp systemd/github-maintainer-console.service ~/.config/systemd/user/
cp systemd/github-maintainer-live.service.example ~/.config/systemd/user/github-maintainer-live.service
cp systemd/github-maintainer-live@.service.example ~/.config/systemd/user/github-maintainer-live@.service
cp systemd/github-maintainer-live.timer.example ~/.config/systemd/user/github-maintainer-live.timer
systemctl --user daemon-reload
systemctl --user enable --now github-maintainer-live.timer
systemctl --user enable --now github-maintainer-console.service

The timer first runs doctor, then starts one live cycle. Each cycle searches GitHub-wide for eligible public Issues, refreshes all open PRs authored by the account, handles one existing PR follow-up before new work, applies the durable human-paced quota, and evaluates the strict merge gate last. The Console queues single-Issue work through github-maintainer-live@<workflow-id>.service; it does not run a second private worker or bypass the shared lock.

Operational boundaries

  • It does not auto-tag releases, delete branches, or force-close issues.
  • Automatic follow-up is limited to one existing PR per cycle and only handles failed checks, requested changes, or a dirty/behind branch. It requires an App-owned head branch, an unchanged head SHA, an isolated worktree, a bounded diff, and all configured validation commands passing before push.
  • Automatic merge requires a non-draft PR with at least one completed check suite, no failed or pending checks, maintainer approval, no outstanding review request, a clean merge state, and an exact head SHA. The base repository must also be owned by the installed App account. External upstream PRs therefore stop at no_merge_candidate until an upstream maintainer merges them.
  • It refuses new work when the global active-PR budget, per-repository budget, daily quota, or negative-health cooldown is exhausted.
  • It fetches the details of existing PRs and classifies failed checks, review requests, conflicts, pending checks, and stale maintenance state before considering new work.
  • Each scan also records read-only repository health: open PR volume, Renovate/ Dependabot-style dependency PRs, latest default-branch workflow state, and recent releases. Current workflow health uses the latest run per workflow; historical failures remain available as audit evidence.
  • It excludes security issues and assigned issues from automatic claiming.
  • It keeps worktrees after a PR opens so follow-up review can continue from the same state.
  • Automatic PR creation is disabled in config.toml; it is enabled only by the reviewed live template and remains blocked until the full live preflight passes.
  • The coding worker receives no short-lived GitHub App token; only the controller performs push, PR comments, and merge requests.
  • A failed follow-up leaves its worktree and manifest for explicit inspection; the next timer does not blindly retry the same failed write.

The controller is a local worker, not a GitHub Action. This keeps the coding agent, local test dependencies, and Obsidian memory loop in one auditable place.

Privacy boundary

Public files use placeholders and relative or systemd-specifier paths. Do not commit config.toml, config.live.toml, live.env, private keys, state, logs, local user identifiers, or repository-specific private context. Before publishing a change, inspect both the working tree and the complete Git history. Treat credentials, user content, local identifiers, and unnecessary account metadata as private by default.

Maintainer Console

Start the local control plane against the live configuration:

python3 console/console_server.py --config config.live.toml

Open http://127.0.0.1:8787. The console reads the latest cycle, shows the PR and issue queue including GitHub-wide candidates, displays repository health, and exposes the same multi-agent stages used by the Live worker. Starting an Issue queues a durable systemd workflow; Scout and Diagnose run in parallel, Implement and Verify work in an isolated checkout, and Writer commits, pushes, creates the PR, and links the source Issue after all gates pass. The panel exposes output, evidence, pause/resume/cancel/retry controls, and the current worker state.

The Backend execution section reads state/execution.json, which is written by the live controller and manual scan controller with a cross-process lock. It shows the current cycle phase, safe command labels, target repository or issue, and recent results while a cycle is running. Tokens, private keys, prompts, and raw command output are never written to this trace.

The Console is a control and observation plane, not a credential holder. The systemd worker owns the external write path, mints a short-lived App token for the process, and enforces the same live preflight and ownership gates for both timer cycles and browser-queued workflows. Pointing the Console at config.toml remains an explicit dry-run choice.

Unified multi-agent workflow

The Console's 启动统一工作流 action and the systemd live cycle both call workflow_engine.py. They share the same durable state machine and Writer:

  1. Scout and Diagnose inspect the repository in parallel with read-only Codex workers.
  2. Arbiter records an evidence-based go/no-go decision.
  3. Implement edits only an isolated worktree and cannot commit or push.
  4. Verify runs every configured validation command plus a read-only diff review.
  5. Communicate drafts the PR body from the verified evidence.
  6. Writer is the only stage allowed to commit, push, create a PR, or comment, and only after the live preflight and ownership gates pass.

Each run is recorded in state/workflows.json with per-Agent status, safe command labels, streamed command/Agent output, artifact paths, stage events, structured implementation and verification previews, and blocking reasons. The live systemd path enables Writer only with config.live.toml, a fresh App installation token, and the explicit live confirmation. Writer checkpoints commit, push, PR creation, and Issue linking so a worker restart resumes idempotently instead of duplicating external writes.

The unified workflow panel is also an operation console. 暂停, 继续, and 取消 persist durable controls consumed by the systemd worker; the browser does not need to own the worker process. Stopped runs can be retried; the retry keeps a retry_of link to the prior evidence. The browser subscribes to /api/workflows/<run-id>/events using SSE and falls back to polling if the stream is unavailable. GET /api/workflows/<run-id>/artifacts/<name> exposes only sanitized state-directory artifact previews for local observation.

About

Human-paced multi-agent GitHub maintenance controller with an operational console

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages