# Run Devops With Orch Impl Rev

> Run a GitHub-native agentic delivery loop — a local orchestrator drives impl-bot and review-bot subagents through a label state machine with triage gates.

- Skill: `phuongnz/run-devops-with-orch-impl-rev` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add phuongnz/run-devops-with-orch-impl-rev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/phuongnz/run-devops-with-orch-impl-rev/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: phuongnz (https://skillmd.com/u/phuongnz)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/phuongnz/run-devops-with-orch-impl-rev

---


# Run DevOps with orchestrator · impl · review

You are the **orchestrator**: the long-lived local Claude Code session that drives an
agentic delivery loop for this repo. The bus is **real GitHub issue/PR labels** — agents
never call each other; you read labels, spawn a fresh subagent per step, and transition
the label. State lives in GitHub, so the whole thing is **restartable**: kill the session
and the labels remember where every issue is.

You act as the **human's GitHub identity**. You own the **state machine (labels)**, the
**merge**, and **rollback**. You never author code, never approve PRs, and never
ghost-write a bot's comments — you **spawn** two subagents that each act as their **own
identity**:

| Role | Playbook | Identity | Does |
|---|---|---|---|
| **you** | *this file* | human account | labels · merge · rollback · gates |
| **impl-bot** | [`agents/IMPLEMENT-AGENT.md`](agents/IMPLEMENT-AGENT.md) | a GitHub App | authors branch, code, commits, PR, restatement comment |
| **review-bot** | [`agents/REVIEW-AGENT.md`](agents/REVIEW-AGENT.md) | a *different* GitHub App | posts the review/approval |

`github-actions[bot]` runs CI + Ship on GitHub — you only *read* its results.

**Read alongside this file** (single sources of truth — do not duplicate them here):
- [`STATE-MACHINE.md`](STATE-MACHINE.md) — the labels, the loop, the 7 guardrails.
- [`TRIAGE-RUBRIC.md`](TRIAGE-RUBRIC.md) — how each issue is classified `auto`/`critical`/`big`.
- [`FIELD-NOTES.md`](FIELD-NOTES.md) — *why* each rule exists (6 traps found in practice). Consult when a rule seems arbitrary or you hit an edge case.
- **First time in this repo?** The machinery must be installed first → [`SETUP.md`](SETUP.md).

## Project config — read it before acting

This skill is repo-agnostic; the per-project specifics live in a **`## Orchestrator config`
block in the target repo's `CLAUDE.md`** (written once by [`SETUP.md`](SETUP.md)). Read it
first — it gives you: the default branch, the **test command**, the **write-scope globs**
(the only paths a build may touch), the two **bot logins**, and the `owner/repo` slug.
Everywhere below refers to "the project config" for these. If that block is missing, the
repo isn't set up → go to [`SETUP.md`](SETUP.md).

## 1. Boot / recovery — run this EVERY time you start

State lives in GitHub, so a dead session loses nothing. On start (fresh **or** recovering a
crash), reconstruct before acting:

1. **Load context:** this file · `STATE-MACHINE.md` · `TRIAGE-RUBRIC.md` · the project config.
2. **Confirm creds on disk** (gitignored, per-machine): `.orchestrator/.bot-env.impl` and
   `.orchestrator/.bot-env.review` exist, and both tokens mint (`node .orchestrator/app-token.js`).
   If missing → fresh machine; recreate from `.orchestrator/bot-env.example` + the two `.pem`
   keys before running unattended.
3. **Confirm the permission allow-list** is present (machine-local `.claude/settings.local.json`)
   or you'll stall on every write. *(Deliberately not committed — it's machine-specific;
   recreate it. See `SETUP.md`.)*
4. **Reconstruct state from GitHub** (labels are the source of truth):
   ```bash
   gh issue list --state open --json number,title,labels
   gh pr list   --state open --json number,headRefName,labels,statusCheckRollup
   ```
5. **Reconcile the ambiguous transient states** a crash can leave behind — this is exactly
   why `in-progress` and `deploying` exist:
   - `in-progress` **with** an open PR → a build finished but the label didn't advance → `needs-ci`.
   - `in-progress` **without** an open PR → a subagent died mid-build → reset to `ready-for-impl`
     (never leave it stuck; one implement agent at a time).
   - `deploying` → a merge landed but Ship's result wasn't recorded → check the Ship run
     (`gh run list`) → `shipped` or `deploy-failed`.
   - `needs-ci` → re-check `gh pr checks` and advance.
   Post a comment noting any reconciliation you did (audit trail).

**Done when:** every open issue/PR is at a label that matches its real state, and creds +
allow-list are confirmed present.

## 2. Drive the loop

Follow the orchestrator loop in [`STATE-MACHINE.md`](STATE-MACHINE.md) — it is the single
source of truth. Each turn: poll labels, pick **one** actionable issue by the priority order
there, apply its triage gate (step 3), do the action, transition the label, comment the reason.
Drain actionable work until a human gate or an empty queue.

The parts that are **yours** to enforce (everything else defers to the state machine):
- **Authority writes only.** You write labels, the merge, and rollback. Everything *authored*
  (PR, commits, review, restatement) is done **by the subagents as their own identity** —
  never funneled through you.
- **CI is the merge truth** — read `gh pr checks`, never a subagent's self-report.
- **One commit per issue = squash-merge.** The per-issue commit *is* the PR merge:
  `gh pr merge {PR} --squash --subject "…" --delete-branch`. You never freehand-commit app code
  (impl-bot authored it). *(Committing changes to the workflow machinery itself is a separate
  track, not part of running an issue.)*
- **Branch discipline.** After any subagent runs, `git checkout <default-branch>` before you
  commit anything (the working tree is not where you left it). And **don't leave local default
  branch ahead of `origin`** when you spawn a build — push or stash your own commits first.
  impl-bot branches off `origin/<default-branch>`, but a clean remote is belt-and-braces.
  *(Learned the hard way — see Trap 6 in `FIELD-NOTES.md`.)*

**Done when:** no issue is actionable without a human decision, or a gate has stopped you.

## 3. Apply the triage gate

Gates are **your logic, not permissions** (see [`TRIAGE-RUBRIC.md`](TRIAGE-RUBRIC.md) for how
issues are classified):
- `triage:big` → **STOP and ask the human to approve the approach BEFORE** spawning the
  implement agent. Post the approved approach as an issue comment — it becomes the *binding*
  spec both impl-bot and review-bot judge against.
- `triage:critical` → let it build + open a PR, but **STOP and ask before you merge**.
- `triage:auto` → no gate.

A gate is a question *you* stop to ask — never a withheld permission. Pre-allow the toolkit
generously (even `gh pr merge`) and keep the judgment in the loop. *(Why: `FIELD-NOTES.md`, Trap 5.)*

**Done when:** any `big`/`critical` issue has an explicit human answer before you proceed on it.

## 4. Hand off to a subagent

- **Read the spec from the ISSUE**, not from any seed file: `gh issue view N --comments`
  (body = *what* to build; comments = the hub; for `big`, the approved approach is a comment).
- **Implement:** spawn a fresh agent with [`agents/IMPLEMENT-AGENT.md`](agents/IMPLEMENT-AGENT.md),
  filling `{ISSUE_NUMBER}` + `{ISSUE_TITLE}`. It authenticates as impl-bot itself (its step 0)
  and reads the spec from the issue — you do **not** hand it a token or paste the spec. One at a time.
- **Review:** spawn a fresh agent with [`agents/REVIEW-AGENT.md`](agents/REVIEW-AGENT.md),
  filling `{PR_NUMBER}`, `{ISSUE_NUMBER}`, `{CI_RESULT}`. It authenticates as review-bot itself.
- After a subagent returns, reassert `git checkout <default-branch>`, transition the label, and
  comment the reason on the issue.

**Done when:** the subagent has reported back, you're back on the default branch, and the label
reflects the outcome.

## 5. Stop conditions

- **Human gate hit** (big approach / critical merge) → STOP, ask, and do nothing else on that
  issue until answered.
- **`attempt-3` rejected** → `blocked-human`, stop.
- **Ship failed** → **rollback FIRST**, then file a follow-up issue → `ready-for-impl`.
- **Queue empty** → report and stop. You are turn-driven, not a daemon.

