|
| 1 | +# PR review demo (policy-gated GitHub comment) |
| 2 | + |
| 3 | +This example shows **why a declarative control plane beats ad-hoc glue code** for agent workflows that touch the outside world. |
| 4 | + |
| 5 | +## What it demonstrates |
| 6 | + |
| 7 | +- **Workflow as data** — `workflows/pr-review.yaml` reads like a runbook: fetch PR context → review → post a comment. |
| 8 | +- **Structured agent output** — the reviewer must return JSON validated by `schemas/review-output.json` (summary + findings), not an unparseable blob. |
| 9 | +- **First-class policy** — `policies/guarded-writes.yaml` lists which tool `uses` strings require explicit approval. |
| 10 | +- **Safe-by-default writes** — the `post_comment` step calls a **simulated** native GitHub tool (`tools/github.yaml`). Without approval, **policy blocks the call** before any side effect. |
| 11 | +- **Traceable behavior** — `agentctl logs` shows normal step progress plus a **`policy.denied`** event on the blocked step. |
| 12 | + |
| 13 | +## Why this matters |
| 14 | + |
| 15 | +In a typical script, “call the model, then maybe post to GitHub” is buried in code paths that are hard to review, diff, and audit. Here, **permissions and sequencing are YAML** you can put in code review, and the runtime **enforces** them with **exit codes** and **SQLite traces**. |
| 16 | + |
| 17 | +## Project layout |
| 18 | + |
| 19 | +| Path | Role | |
| 20 | +|------|------| |
| 21 | +| `project.yaml` | Imports resources; defaults to `mock` model (no API keys). | |
| 22 | +| `workflows/pr-review.yaml` | Three steps: `fetch_pr`, `review_diff`, `post_comment`. | |
| 23 | +| `agents/reviewer.yaml` | Reviewer instructions + output schema. | |
| 24 | +| `tools/github.yaml` | `native` tool; operations are implemented offline in the binary. | |
| 25 | +| `policies/guarded-writes.yaml` | Requires `--approve` for `tool.github.pull_request.post_comment`. | |
| 26 | +| `schemas/*.json` | JSON Schema for workflow input and agent output. | |
| 27 | +| `fixtures/sample-pr.json` | Sample input (no GitHub network or tokens). | |
| 28 | + |
| 29 | +## Prerequisites |
| 30 | + |
| 31 | +Build `agentctl` from the repo root (`make build`) or use a release binary on your `PATH`. |
| 32 | + |
| 33 | +## How to run |
| 34 | + |
| 35 | +From the **repository root** (paths below assume that): |
| 36 | + |
| 37 | +```bash |
| 38 | +agentctl validate --project examples/pr-review-demo |
| 39 | +agentctl plan --project examples/pr-review-demo --state /tmp/pr-review-state.db |
| 40 | +agentctl apply --project examples/pr-review-demo --state /tmp/pr-review-state.db --auto-approve |
| 41 | +``` |
| 42 | + |
| 43 | +### Default run (comment blocked) |
| 44 | + |
| 45 | +```bash |
| 46 | +agentctl run workflow/pr-review \ |
| 47 | + --project examples/pr-review-demo \ |
| 48 | + --state /tmp/pr-review-state.db \ |
| 49 | + --input-file examples/pr-review-demo/fixtures/sample-pr.json |
| 50 | +``` |
| 51 | + |
| 52 | +- Exit code **5** = policy denial (by design). |
| 53 | +- Inspect the trace (use the printed **Run ID**): |
| 54 | + |
| 55 | +```bash |
| 56 | +agentctl logs --project examples/pr-review-demo --state /tmp/pr-review-state.db --run <run-id> |
| 57 | +``` |
| 58 | + |
| 59 | +You should see steps through `review_diff`, then **`policy.denied`** on `post_comment` with reason **`approval_required`**. |
| 60 | + |
| 61 | +### Optional: allow the write (full success) |
| 62 | + |
| 63 | +```bash |
| 64 | +agentctl run workflow/pr-review \ |
| 65 | + --project examples/pr-review-demo \ |
| 66 | + --state /tmp/pr-review-state.db \ |
| 67 | + --input-file examples/pr-review-demo/fixtures/sample-pr.json \ |
| 68 | + --approve tool.github.pull_request.post_comment |
| 69 | +``` |
| 70 | + |
| 71 | +This records a simulated comment result (still **no** real GitHub traffic). |
| 72 | + |
| 73 | +## Expected highlights |
| 74 | + |
| 75 | +1. **`fetch_pr`** completes — native tool normalizes the `pr` object from input JSON. |
| 76 | +2. **`review_diff`** completes — mock model returns fixed structured JSON that satisfies the schema. |
| 77 | +3. **`post_comment`** is **blocked** unless approved — CLI prints a clear **policy** line naming the gated `uses` string. |
| 78 | + |
| 79 | +## Design note (no real GitHub) |
| 80 | + |
| 81 | +`pull_request.fetch` and `pull_request.post_comment` are **offline** native operations: they exist so the workflow and policy strings look like a real integration while the demo stays repeatable in CI and on laptops without tokens. |
| 82 | + |
| 83 | +## Compared to one-off code |
| 84 | + |
| 85 | +| Concern | This demo | Typical script | |
| 86 | +|--------|-----------|----------------| |
| 87 | +| Order of operations | Workflow YAML | Implicit control flow | |
| 88 | +| “Can we post?” | Policy resource + trace | Easy to forget a guard | |
| 89 | +| Model output shape | JSON Schema on the agent | String parsing / hope | |
| 90 | +| Audit trail | `agentctl logs` | Printf / none | |
| 91 | + |
| 92 | +For broader patterns, see [`docs/EXAMPLES.md`](../../docs/EXAMPLES.md) and [`docs/DESIGN_DOC.md`](../../docs/DESIGN_DOC.md). |
0 commit comments