Chapter 15 · Delivery

Delivery & CI Governance

Chapters 13 and 14 are about the quality of the agent. This chapter is about the quality of the changes you make to the harness itself. The repo ships with a set of CI/CD gates — the delivery rails — built to the Intent Driven Development delivery standard. One rule underpins all of them: the agent proposes, a gate disposes. An AI (or a human) can author any change it likes; nothing reaches main until a mechanical or human gate has had its say.

i
The operator's guide lives in the repo

This page is the developer-facing tour. The authoritative operator's runbook — exact go-live steps, residual-risk analysis, and the shakedown procedure — is .github/RAILS.md, which implements the Intent Driven Development delivery standard (its rails and section numbers trace back to a the-rails.md specification). When this page and RAILS.md disagree, RAILS.md wins.

The six gates

Six gates stand between a change and main. Five of them run in GitHub Actions when you open a pull request. The sixth runs on your own machine, and stops an agent from even finishing its turn on a broken build.

There are three distinct levels of "seriousness" here, and it is worth separating them before you read the table, because the words look similar and mean very different things:

  • Required — GitHub itself refuses the merge button. build-and-test, the OWASP gate, and security-review are in this set today.
  • Blocking, but not yet required — the check genuinely fails, and it fails closed if it cannot finish, but branch protection is not yet configured to insist on it. So in practice it can be merged past. correctness-review sits here deliberately, until the agentic gates are taken live (see below).
  • Advisory — it leaves a comment and never fails. The grader is the only one of these.
GateWhereFires onBlocks or advises
build-and-test.github/workflows/ci.ymlEvery PR + push to mainBlocks — build must compile, full xUnit suite must pass.
OWASP Agentic Top-10 Gate.github/workflows/ci.ymlEvery PR (after build)Blocks — any Verdict.Fail in the 10-row eval pack fails the merge.
security-review.github/workflows/security-review.ymlEvery PR; reviews only when a gated path or risk:high label is presentBlocks on HIGH — the security-reviewer agent writes a PASS/BLOCK verdict; HIGH fails closed.
correctness-review.github/workflows/correctness-review.ymlEvery non-draft PR; reviews only when compilable source under src/ changedBlocks (capable), not yet required — a fresh agent hunts the diff for logic defects and writes a CORRECTNESS_VERDICT: PASS/BLOCK; a BLOCK fails the check (overridable by the audited accepted-risk:correctness label — but only for a real finding; the label deliberately cannot override a review that never finished). Fails closed if it can't complete. Held out of the required-check set until go-live.
grader.github/workflows/grader.ymlEvery non-draft PRAdvises — a fresh agent grades the diff against the PR's stated intent and comments. Never blocks.
Stop gate.claude/hooks/stop-build-gate.ps1An agent tries to end a Claude Code turn locallyBlocks a red build before it ever becomes a commit.

Blocking vs advising — and why the split matters

A blocking gate is a mechanical or agentic check whose verdict the branch protection ruleset trusts to gate merge. An advising gate produces a signal for a human reviewer (the "Checker") to weigh, but cannot itself stop a merge. The grader is deliberately advisory: "the grader ran" is what the methodology requires; what it said is the human's call. Making a judgment-heavy LLM verdict a hard gate would either rubber-stamp or block-at-random — neither is honest.

Branch protection (.github/rulesets/main-branch-protection.json) makes exactly the three blocking CI checks mandatory and additionally requires a non-author approval plus a code-owner review. The required check contexts are:

build-and-test
OWASP Agentic Top-10 Gate
security-review

The grader is intentionally not in that list.

How a change flows

┌─────────────────────────────────────────────────────────────────┐
│  LOCAL (your machine)                                            │
│    edit code ──▶ agent tries to finish its turn                  │
│                   └▶ Stop gate: build must be green, else REFUSE │
│    commit ──▶ push branch ──▶ open PR                            │
├─────────────────────────────────────────────────────────────────┤
│  CI (GitHub Actions, on the PR)                                  │
│    build-and-test ──────────────▶ blocks on red build/test      │
│    OWASP Agentic Top-10 Gate ───▶ blocks on any eval Fail        │
│    security-review ─────────────▶ blocks on HIGH (gated paths)   │
│    correctness-review ──────────▶ blocks on logic defect (src/)  │
│    grader ──────────────────────▶ comments a verdict (advisory)  │
├─────────────────────────────────────────────────────────────────┤
│  MERGE                                                           │
│    branch protection: 3 required checks green                   │
│                     + non-author approval + code-owner review   │
└─────────────────────────────────────────────────────────────────┘

The three agentic gates

The grader, security-review, and correctness-review gates each run a Claude agent inside CI via anthropics/claude-code-action@v1. Each reads a committed rubric and follows it exactly — the rubric is the contract, version-controlled alongside the code it judges:

GateRubricWhat it checks
grader.github/grader-rubric.mdDoes the diff do what the PR description claims? Scope creep, standards (file length, XML docs, Result<T>, keyed-DI tools, exception handling).
security-review.github/security-review-rubric.mdInjection, broken access control, secret leakage, unsafe crypto on the gated files (Auth / Identity / Security / Migrations / infra/ / .github/).
correctness-review.github/correctness-review-rubric.mdPlain logic and correctness defects the other rails miss — scoped to the changed-line anchor set under src/. Blocks only on a high-confidence defect.

The security-review gate deserves a closer look, because it behaves in a way that looks contradictory until you see the reasoning. It is a required check, so it runs on every single PR — but it only actually reviews anything when a security-sensitive path changed, or when someone applied the risk:high label.

That gives it three possible outcomes:

  • Nothing sensitive changed → it reports success immediately without reviewing. It has to report something, or a required check would hang forever on routine PRs.
  • Something sensitive changed, review runs → you get a real verdict.
  • Something sensitive changed, but the review can't run — say the API key isn't configured yet → it fails closed. It blocks. An unreviewable security change is never waved through on the grounds that the reviewer was unavailable.

The local gate: the Stop hook

The cheapest place to catch a broken build is before it leaves your machine. The Stop hook (.claude/hooks/stop-build-gate.ps1) runs when an agent attempts to end a Claude Code turn: if the solution does not build, the hook refuses and hands the build error back to the agent. The agent fixes it and tries again. By the time a commit exists, it already builds — so CI's build-and-test is a backstop, not the first line of defence.

Taking the rails live

The rails are wired but dormant by default — the two agentic gates need credentials a human must provision. These are deliberate, outward-facing actions; nothing in the repo performs them for you. (Until they're done, the security-review gate fails closed on gated PRs by design, and the grader stays green as a no-op.)

  1. Install the Claude GitHub App on the repo — run /install-github-app in Claude Code, or install from github.com/apps/claude. Repo admin required.
  2. Add the ANTHROPIC_API_KEY repository secret (Settings → Secrets and variables → Actions). The grader and security-review steps call the Claude API; there is a real per-PR token cost.
  3. Apply branch protection from the version-controlled ruleset — never by hand-editing rules in the GitHub UI:
    scripts/rails/apply-branch-protection.sh --dry-run   # review the plan
    scripts/rails/apply-branch-protection.sh             # apply (prompts to confirm)
    Edit the JSON, re-run the script — that is the only sanctioned path to changing protection.
!
Solo-repo accommodation

GitHub forbids approving your own PR, so on a single-maintainer repo the "non-author approval" rule can't be self-satisfied. The ruleset ships armed with a repo-admin owner bypass set to bypass_mode: pull_request — you can self-merge today, but even the owner cannot push directly to main skipping CI. The moment a second collaborator (or a review bot) joins, remove the bypass actor from main-branch-protection.json and re-apply.

Gate integrity (known residual risk)

These workflows trigger on pull_request, which runs the PR's own copy of the workflow, the rubric, and the gated-path regex. A same-repo branch could in principle try to weaken its own gate. Two mitigations are in place, plus one real closure:

  • The security verdict file is written and read outside the working tree, and any copy a PR committed into the tree is deleted before review — so a planted PASS can't satisfy the gate.
  • Changes to .github/** are themselves a gated path, so weakening a rail requires code-owner review.
  • The real closure is a non-author review of rails changes — exactly what branch protection enforces once a second reviewer exists.
Never use pull_request_target for these gates

It would expose repository secrets and the write token to forked-PR code. The gates run on pull_request on purpose; keep them there.

Prove the rails — the shakedown

A pipeline that has never caught anything is not proven, only present. Before trusting the rails, force each one to fail and confirm it's caught:

GateHow to break it on purposeExpected
Stop gateBreak a .cs file under src/, then try to end a turn.Hook refuses, hands back the build error.
graderOpen a PR whose description claims something the diff does not do.Grader comment calls out the mismatch.
security-reviewThrowaway PR touching a gated path (e.g. a comment under **/Auth/) with a planted HIGH issue.Check goes red. Close it unmerged.
correctness-reviewThrowaway PR that changes a .cs file under src/ with an obvious logic defect (e.g. an inverted condition).Check goes red with a CORRECTNESS_VERDICT: BLOCK comment. Close it unmerged.
CI / OWASPAlready exercised by every real PR.

The docs have a rail too

Documentation drifts faster than anything else in a template repo, so it gets its own rail. Here is what it does, step by step:

  1. .github/workflows/docs-drift-check.yml fires on every push to main, filtered to code, workflow, and governance changes.
  2. It hands the merge diff to a Claude agent, along with the documentation map in .github/docs-drift-rubric.md.
  3. The agent asks one question: did this change ship something the docs still describe the old way?
  4. If so, it opens a pull request containing the doc updates — for a human to review and merge.

This is an advising rail. It proposes a doc PR; a human still disposes. And like the other agentic gates, it stays dormant until the Claude GitHub App and ANTHROPIC_API_KEY are configured.

What's deferred (and where to read about it)

The rails govern getting a change onto main. They deliberately do not cover deploy/promotion, rollback rehearsal, IaC apply with what-if/policy funnels, or production secret rotation — there is no cloud deployment yet. That target topology (Container Apps, Bicep, canary promotion, cost tiers) is documented as the forward plan in the Architecture Guide → Operations & Cost page; it describes what a consumer would build on Azure, not what this repo ships today.

Where to go next

  • Read .github/RAILS.md — the operator's runbook this page summarizes.
  • Cross-reference Chapter 13 — Evaluation Framework: the OWASP Agentic gate runs an eval pack through that same harness.
  • See Chapter 10 — Observability & Safety for the runtime governance (content safety, autonomy tiers) the rails complement at delivery time.
  • The Architecture Guide's Operations & Cost page covers the deferred cloud delivery topology.