Agent stack build plans · № 4 · Hermes CI/CD healer

The prompts

Five stages. Stage 2 is the unusual one: it’s not a prompt you paste into a chat — it’s the prompt that ships in your config and fires itself at 2am.

Stages 1, 3 and 4 go to whatever agent is helping you build the stack — Claude Code, or Hermes itself running interactively on the server. Stage 2 is the ci-heal route’s prompt template, pasted into ~/.hermes/config.yaml. Stage 5 is the series constant: human eyes before trust.

1 · Build the route config

Read the Hermes webhook docs (hermes-agent.nousresearch.com/docs — user-guide, messaging, webhooks) and edit ~/.hermes/config.yaml for me:

1. Under platforms.webhook.extra: port 8644, secret read from WEBHOOK_SECRET in ~/.hermes/.env, keep rate_limit at the documented default of 30, and set a sane max_body_bytes.
2. Add a route named ci-heal that accepts GitHub workflow-run failure events for the repo REPO_OWNER/REPO_NAME only, and filters OUT any run whose head branch starts with "ci-heal/" — that filter is the loop-breaker, do not skip it.
3. Set deliver targets: github_comment, plus telegram.
4. Leave the route's prompt template as the placeholder PROMPT_GOES_HERE — I will paste it myself in the next step.
5. Generate the secret with openssl rand -hex 32 into ~/.hermes/.env, and print exactly what I should enter in GitHub's webhook settings (payload URL, content type, secret, which events).

Use only config keys that appear in the docs. If a key I named doesn't exist in this release, stop and show me what the docs say instead of guessing.

2 · The route prompt template — the artifact itself

A GitHub Actions workflow just failed on {workflow_run.head_branch} of {repository.full_name} (run id {workflow_run.id}). You are the CI healer. Work entirely inside your Docker sandbox.

1. FETCH — this payload contains no logs. Run: gh run view {workflow_run.id} --log-failed --repo {repository.full_name}. Read only the failed jobs; if the output is huge, work from the last 200 lines per failed step.
2. REPRODUCE — clone the repo, check out {workflow_run.head_branch}, install deps, and run the failing tests locally. If you cannot reproduce the failure, STOP and report "not reproducible locally" with your best diagnosis — do not patch what you cannot see fail.
3. FIX THE CODE, NEVER THE TEST — you may not delete, skip, weaken, or xfail any test, and you may not touch CI workflow files. If the correct fix genuinely is a test change, say so in your report and stop.
4. VERIFY — run the full test suite in the sandbox. Anything less than fully green: no PR, diagnosis only.
5. PR — create a branch ci-heal/{workflow_run.id}-short-slug, commit with a conventional message, open a PR against {workflow_run.head_branch} with gh pr create. Body: one-paragraph diagnosis, what changed and why, full local test summary. Never push to {workflow_run.head_branch} itself, never touch the default branch, never merge anything.
6. REPORT — one short summary through your delivery targets. When quoting logs, strip anything that looks like a token, URL with credentials, or connection string.

One attempt. If the failure looks like infrastructure (timeouts, rate limits, external outage), skip steps 3–5 and report the flake.

Template fields mirror the GitHub payload JSON; the syntax shown in the Hermes docs is {pull_request.title}-style. Check the webhooks doc for the exact field paths your release exposes on workflow events before shipping this.

3 · Shakedown on a scratch failure

Before I trust this overnight: I've pushed a deliberately broken commit to a scratch branch and let the workflow fail. Walk me through the delivery end to end using the gateway logs:

1. Confirm the webhook arrived and the X-Hub-Signature-256 check passed.
2. Show me every terminal command the run wants, one at a time — approvals are in manual mode and I'll approve each by hand.
3. Confirm the run happened in the docker backend, not local (if any command ran on the host, stop everything and show me the terminal config).
4. When the PR opens, show me the diff and the sandbox test output side by side.
5. List everything that surprised you or needed a retry, so I can fix the image or the route before real failures hit this.

4 · Tighten the blast radius

The shakedown passed. Now harden it before I switch approvals.mode from manual to smart:

1. approvals.deny: add globs denying gh pr merge, git push --force (and -f), any push to the default branch, and gh repo delete. Show me the final list.
2. Verify the ci-heal/* filter actually fires: redeliver a failure event from one of the agent's own PR branches and confirm the route drops it without starting a run.
3. Confirm terminal.env_passthrough exposes ONLY the GitHub token to the sandbox — nothing else from ~/.hermes/.env leaks into child processes.
4. Tell me the token cost of the shakedown run, and estimate a bad month: 30 nightly failures on this repo at that size. If the number is uncomfortable, propose a cheaper triage model for this route — Hermes is model-agnostic, use that.

5 · Human eyes before you trust it — the twocents step

Before this thing opens PRs at my team unsupervised, they get a say.

Build a single self-contained HTML page from the shakedown: the overnight timeline (failure → webhook → logs fetched → sandbox → diagnosis → green suite → PR), the PR body it wrote, and the full diff.

Then add the twocents MCP server:
  claude mcp add --transport http twocents https://twocents.page/mcp
(restart the session so the tools load). Call share_page with the full contents of that page and give me the review link — I'll drop it in the team channel. They pin notes directly on the page, no login: on the diff, on the PR tone, on whether the no-merge line is in the right place. When the notes are in, call get_feedback with the room id, show me each note, and apply what I approve — to the route prompt, the filters, or the deny list.

Why stage 5 exists: after a week of building this you will read the agent’s PRs the way a parent reads their kid’s essay — fondly. Your teammates will read them the way they read any PR — suspiciously. Their reading is the one that predicts whether these PRs get merged or muted, and it’s cheaper to learn that from notes on a mock than from a month of ignored bot PRs.

Disagree? Pin a note on it →