QA plan: Prod environment manual gate (#28)
Story: Prod environment — manual-approval gate (GitHub Environment) (#28)
Capability: Environments and deployment promotion (#26)
ADR: RAG001 — Prod gated by GitHub Environment protection rule
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| Pipeline behavior | Prod deploy job does NOT run automatically on merge to main; it waits for a named reviewer to approve |
| Pipeline behavior | Prod deploy job runs from the same artifact (Lambda zip, SPA bundle) produced by the QA deploy job — no rebuild |
| Artifact integrity | Pipeline step diffs the SHA256 of the Lambda zip and SPA bundle between QA and Prod jobs; they must be byte-identical |
| Security | Prod GitHub Environment has at least one required reviewer |
| Security | Prod OIDC role trust policy names repo:ArunskiOrg/rag-sample-app:environment:prod; no wildcard in sub |
| Security | Terraform state backend for Prod is separate from QA; QA OIDC role cannot assume the Prod OIDC role or write to Prod state |
| Integration (manual) | After Prod approval and apply: GET /healthz on Prod API base URL returns 200 |
Entry criteria
Section titled “Entry criteria”- Story #27 (QA auto-deploy) complete and passing
- Prod GitHub Environment created with a required reviewer
- Prod OIDC role exists in AWS with the
prodenvironment-scoped trust policy
Exit criteria
Section titled “Exit criteria”gh api repos/ArunskiOrg/rag-sample-app/environments/prodconfirms at least one required-reviewer rule- Artifact hash diff step passes (QA zip SHA == Prod zip SHA)
- OIDC trust policy parsed in CI: Prod trust policy references
environment:prodwith no wildcard - Prod state bucket policy parsed: denies
s3:PutObjectfrom the QA OIDC role ARN - Manual smoke:
/healthz200 in Prod after approved deploy
Environments
Section titled “Environments”CI (GitHub Actions) for gate and artifact checks. Prod AWS env for deployment validation.
Tooling
Section titled “Tooling”GitHub Actions, gh CLI, sha256sum in pipeline, Terraform, AWS CLI.
How tests are written
Section titled “How tests are written”# Given the Prod GitHub Environment is configured# When its protection rules are fetchedgh api repos/ArunskiOrg/rag-sample-app/environments/prod \ | python3 -c "import sys,json; e=json.load(sys.stdin); rules=e.get('protection_rules',[]); has_reviewer=any(r['type']=='required_reviewers' for r in rules); assert has_reviewer, 'Prod env missing required reviewer'"# Then at least one required-reviewer rule is presentArtifact hash check:
# Given the Lambda zip was uploaded as a CI artifact by the QA job# When the hash is compared between QA artifact and Prod artifactdiff <(sha256sum qa-artifacts/api.zip | awk '{print $1}') <(sha256sum prod-artifacts/api.zip | awk '{print $1}')# Then diff exits 0 (files are identical)- An emergency hotfix may pressure the operator to bypass the manual gate. Procedure: create a hotfix branch, promote through QA fast-track (same pipeline), then approve Prod deploy immediately. Do not bypass the environment gate.
- Prod state bucket policy must be refreshed when a new QA OIDC role ARN is created (e.g., on Terraform destroy/recreate). Add the bucket policy assertion to the Prod bootstrap runbook.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”RAG001: Prod is gated by a GitHub Environment manual-approval rule; artifacts are byte-identical between QA and Prod. Security per-feature requirements (#26–#28): Prod OIDC role environment-scoped, Prod state unwritable from QA role. All exit criteria map directly.
© 2026 Benjamin Arunski