
A fast patch still needs evidence. Build a Rails CI path that exposes failures, checks security assumptions and leaves the approval decision with a human reviewer.
GitHub Copilot for Rails Engineers · Part 04
In Part 03, we gave an agent a narrow CaseFlow status-transition issue and produced a local candidate patch. An agent can propose a route, controller and request specs. Those files tell us what might happen; they do not tell us what actually happened on a runner, whether dependencies are known or who may authorize the change. Here we design a chain of checks that produces inspectable evidence while preserving the human decision.
The companion repository contains the integrated Rails API, the Part 03 patch, a staged GitHub Actions workflow, Mermaid source, a lab workbook with answers and a review worksheet. This educational workflow has not run in GitHub Actions. There is no Gemfile.lock in the supplied baseline, and no successful RSpec, security scan or PR run is claimed. The illustration shows conceptual review artifacts, not a GitHub screenshot or observed CI result.
Contents
- Start with a truthful evidence boundary
- Make the Rails test gate reproducible
- Add a security layer that says what it covers
- Read failures as evidence
- Keep the merge decision human
- Run the 45-minute lab
- References and supporting material
Start with a truthful evidence boundary {:#boundary}
A PR has different kinds of claims. A spec file contains assertions. A workflow file contains planned commands. A job log shows observed execution on a given commit and environment. A reviewer decides whether those observations and the product policy justify the change. None substitutes for the others.
| Layer | CaseFlow artifact | What it can establish | What it cannot establish |
|---|---|---|---|
| Intent | Part 03 ISSUE.md in the companion repository |
Proposed transition and exercise assumptions | Actual customer authorization |
| Source | candidate.patch |
Exact proposed route, action and request specs | Test pass or deployability |
| Runner | Proposed rails-pr.yml
|
Commands that would run after setup | An observed green status until it runs |
| Human | Review worksheet | Explicit disposition and residual risk | Guarantee that no defect exists |
The link to the companion ISSUE.md appears in the support materials. GitHub’s Copilot review guidance calls for human review of agent-produced changes. In the CaseFlow exercise, approving a policy for who can resolve a request remains outside the authority of an automated test.
The arrow into review carries observed results, including failures. Green in this diagram identifies the type of evidence, not a passing CaseFlow run. The matching Mermaid source is included as a local .mmd file in the package.
Make the Rails test gate reproducible {:#test-gate}
CaseFlow’s baseline Gemfile declares Rails ~> 8.0.0, SQLite, Puma and RSpec. It has no Gemfile.lock. Installing from that Gemfile could resolve different compatible versions on different days. Before requiring a CI check, on a Mac with Ruby installed, resolve and commit a lockfile in the companion repository’s caseflow/ directory, review the resolved gems, then run the app locally:
cd caseflow
bundle install
RAILS_ENV=test bin/rails db:prepare
bundle exec rspec spec/requests/api/requests_spec.rb
Do not paste an invented lockfile into the project. If bundle install fails, record the Ruby version, command and first actionable error; a failed setup is evidence, not a passed test. Apply Part 03’s candidate patch before expecting its four proposed transition cases in RSpec. The unpatched baseline runs only its original request specs.
The supplied staged workflow illustrates the first useful GitHub Actions job. It is stored outside .github/workflows/ so this package does not silently activate a check in any repository. The central excerpt is:
permissions:
contents: read
jobs:
rails_specs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: caseflow
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- run: bundle config set frozen true
- run: bundle install
- run: RAILS_ENV=test bin/rails db:prepare
- run: bundle exec rspec spec/requests/api/requests_spec.rb
bundle config set frozen true makes a missing or inconsistent lockfile fail early. The sample targets Ubuntu and a Ruby 3.3 interpreter; verify that version against your local app and choose pinned action SHAs under your organization’s policy before production. GitHub’s Ruby Actions guide documents ruby/setup-ruby, Bundler and caching. After a checked-in lockfile and first reliable run, bundler-cache: true can shorten future jobs; it does not prove the code is secure.
Add a security layer that says what it covers {:#security}
“DevSecOps passed” is too vague for review. Match each proposed gate to a known question:
| Question | Candidate check | Important limit |
|---|---|---|
| Do request specs catch this transition? | RSpec positive and negative cases | Assertions are only observed after a run; specs can miss policy defects. |
| Did a new dependency introduce known risks? | GitHub dependency review on PRs; review Gemfile.lock diff |
Requires repository support and correctly configured workflow; no new gem means little differential signal. |
| Does Rails source contain recognizable risky patterns? | Brakeman after adding it to the reviewed Gemfile and lockfile | Static analysis can miss domain authorization errors. |
| Are code paths flagged by code scanning? | CodeQL default setup where available | Configuration/eligibility and findings need review; do not claim it ran here. |
| Can this actor resolve that request? | Product decision + auth design + negative tests | A shared operator token in the lab is not production identity policy. |
GitHub describes dependency review as a PR dependency-diff check, and CodeQL default setup as repository code scanning configuration. Their availability and billing can vary with repository settings; inspect the target repository before making either a required check. The security worksheet lists the exact decision and evidence fields, rather than assuming a green badge.
Keep the sample workflow on pull_request with contents: read; it does not request secrets or a write token. GitHub’s secure use reference discusses risks of untrusted PR code and least privilege. Do not change it to pull_request_target while checking out and running a contributor’s untrusted code.
Read failures as evidence {:#failure}

The empty lockfile position is a teaching cue. There is no recorded failing CaseFlow CI run in this package. A missing Gemfile.lock, failing bundle install, an RSpec assertion failure and a security finding mean different things. Capture which command failed, on which commit, in which environment, then triage the smallest cause. Do not mark a spec “fixed” merely because the agent changed the expectation to fit its implementation.
A practical failure record uses four fields: commit SHA, command, observed exit status or blocker, and next owner/action. When setup fails, downstream scans are not run, not “passed”. If RSpec fails for an unauthorized request, inspect the proposed token guard and the request spec; if the product policy is unclear, return to the issue instead of weakening the test. The worksheet includes examples labeled illustrative rather than fabricated logs.
Keep the merge decision human {:#review}
Require the reviewer to read the three changed files from Part 03 and decide whether open → resolved and the shared operator token model the intended business rule. Even a real green RSpec and static scan would answer only their defined checks. Attach the run URL and commit SHA to the review note, describe unchecked cases (concurrency, token rotation, actor identity, auditing), then approve the lab proposal, request changes or escalate policy. Do not turn the synthetic review into a real customer release decision.
Run the 45-minute lab {:#lab}
- Predict (5 min): before opening the workflow, name two ways a job can fail without running any specs.
-
Prepare (12 min): in a clean checkout, apply
parts/03-issue-to-pr/candidate.patch; generate and inspectcaseflow/Gemfile.lockon your Mac; never commit secrets. -
Observe (12 min): run the focused RSpec command and record the exact outcome or first blocker. Read the staged
parts/04-ci-and-devsecops/rails-pr.yml; compare each command with what your machine actually ran. - Decide (10 min): fill the review worksheet. Which missing decision prevents calling the token guard production authorization?
- Transfer (6 min): choose a separate Rails PR from your experience and identify one missing negative case and one security check with a clear limitation.
The workbook with worked answers supplies prediction questions, a failure triage exercise and an answer key. If Ruby is unavailable, complete the source review and write “not run: no Ruby”; that is a valid, honest lab result.
References and supporting material {:#references}
-
Download complete companion repository and Part 04 materials. Start at
README.md→parts/04-ci-and-devsecops/README.md→docs/posts/04-ci-and-devsecops/article.md. - Companion paths:
parts/03-issue-to-pr/ISSUE.md,parts/03-issue-to-pr/candidate.patch,parts/04-ci-and-devsecops/rails-pr.yml,parts/04-ci-and-devsecops/REVIEW-GATES.md,docs/labs/04-ci-and-devsecops-workbook.md,docs/diagrams/04-ci-evidence.mmd, anddocs/posts/04-ci-and-devsecops/post-manifest.md. - GitHub Docs: Building and testing Ruby; ruby/setup-ruby README.
- GitHub Docs: Configuring the dependency review action; CodeQL default setup.
- GitHub Docs: Secure use of Actions; Review Copilot output.