mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
Some checks are pending
Validate and deploy / Validate deployable source bundle (push) Waiting to run
Validate and deploy / Deploy shoc-backend-dev through Terraform (push) Blocked by required conditions
Validate and deploy / Deploy shoc-backend-staging to Elastic Beanstalk (push) Blocked by required conditions
* chore(terraform): remove tf-poc rehearsal * chore(terraform): drop tf-poc from live module and CI * chore: clean remaining tf-poc reference from `shared_certificate_arn`
154 lines
8.3 KiB
Markdown
154 lines
8.3 KiB
Markdown
# Quality Gates (canonical)
|
||
|
||
> **Status: canonical.** This document owns *how* the architecture and
|
||
> code-quality rules are enforced: the gate list, the local command, the CI
|
||
> mapping, and the pass/fail/skip reporting semantics. The *meaning* of each
|
||
> rule lives in `ARCHITECTURE_AND_CODE_QUALITY.md`.
|
||
>
|
||
> **CI and local run the same check.** Every gate below is executed by
|
||
> `scripts/governance-check.sh`. The `architecture-quality` workflow calls that
|
||
> script so a green run means the same thing locally and in CI. Do not add a
|
||
> check to CI that is not also runnable locally through the script (and vice
|
||
> versa).
|
||
|
||
## Gate inventory
|
||
|
||
| # | Gate | Protects (architecture §) | Local command | CI step |
|
||
|---|------|---------------------------|---------------|---------|
|
||
| G1 | Restore | build integrity | `dotnet restore SeaHavenIndustries.sln` | `architecture-quality` → `governance-check.sh` |
|
||
| G2 | Architecture boundary tests | §1, §3 (dependency direction) | `dotnet test Api.SeaHavenIndustries.Tests ... --filter FullyQualifiedName~ArchitectureTests --no-restore` | same script |
|
||
| G3 | Changed-file formatting | §1 (conventions) | `dotnet format SeaHavenIndustries.sln --no-restore --verify-no-changes --include <changed .cs>` | same script |
|
||
| G4 | Release build | compile correctness | `dotnet build SeaHavenIndustries.sln -c Release --no-restore` | `architecture-quality` and `ci` |
|
||
| G5 | Full test suite | behavior | `dotnet test SeaHavenIndustries.sln -c Release --no-build` | `architecture-quality` and `ci` |
|
||
| G6 | Migration gates | §7 | see §"Migration gates" below | on-demand / release |
|
||
| G7 | Cancellation forwarding | §6 | behavior tests on changed I/O paths + analyzer | review-enforced on changed paths |
|
||
| G8 | Error disclosure | §5 | `SanitizedErrorsTests` (part of G5) | `ci` |
|
||
| G9 | Board-backed regression | review framework | `REVIEW_AND_PR_FRAMEWORK.md` inventory | review-enforced |
|
||
| G10 | Terraform import plan safety | live infrastructure adoption | `python scripts/test-terraform-import-plan-check.py` | `architecture-quality` → `governance-check.sh` |
|
||
| G11 | Terraform static validation | import configuration integrity | commands below | `ci` on the matching PR base |
|
||
| G12 | Terraform release plan safety | dev application CD version_label | `python scripts/test-terraform-release-plan-check.py` | `architecture-quality` → `governance-check.sh` |
|
||
|
||
## How to run locally
|
||
|
||
```bash
|
||
# Complete local repository gate (mirrors the architecture-quality workflow):
|
||
bash scripts/governance-check.sh
|
||
|
||
# By default it compares against the default branch for changed-file formatting.
|
||
# Override the comparison point:
|
||
BASE_REF=origin/dev bash scripts/governance-check.sh
|
||
BASE_REF=main bash scripts/governance-check.sh
|
||
```
|
||
|
||
The script:
|
||
1. `dotnet restore` (G1)
|
||
2. runs the `ArchitectureTests` filter with `--no-restore` (G2)
|
||
3. computes changed `.cs` files vs `BASE_REF` (default `origin/dev`) and runs
|
||
`dotnet format --verify-no-changes --include ...` (G3). When there are no
|
||
changed C# files it skips G3 with an explicit "skipped: no changed C#" line.
|
||
4. builds the complete solution in Release with no restore (G4).
|
||
5. runs the complete solution test suite in Release with no rebuild (G5).
|
||
6. verifies that the Terraform plan guard rejects create, delete, replacement,
|
||
unmanaged resource types, and updates not allowlisted by exact address (G10).
|
||
7. verifies that the release plan guard accepts only a version-only update of
|
||
`module.environment.aws_elastic_beanstalk_environment.this` (G12).
|
||
|
||
G10 permits only exact approved resource address/type pairs for the
|
||
environment-owned boundary: Elastic
|
||
Beanstalk environment, IAM role/inline policy/managed-policy attachment/
|
||
instance profile, Secrets Manager secret metadata, and Route 53 record.
|
||
Initial mode permits no update. Controlled mode requires one
|
||
`--allow-update-address` argument per reviewed in-place update. Every invocation
|
||
also requires `--environment dev` or `--environment staging`; an empty or
|
||
incomplete environment plan fails.
|
||
|
||
G11 runs `terraform fmt -check -recursive`, `terraform init -backend=false`,
|
||
and `terraform validate`. PRs to `dev` validate `live/dev`.
|
||
PRs to `staging` validate only `live/staging`. Org-baseline CloudFormation owns
|
||
the HCP role substrate, and Terraform owns the dev deploy role, so no backend
|
||
CDK or bootstrap root remains in the matrix.
|
||
|
||
G12 accepts only a local or downloaded plan JSON whose sole managed update is
|
||
`module.environment.aws_elastic_beanstalk_environment.this` with
|
||
`version_label` as the only changed attribute. Counts of `0` add / `1` change /
|
||
`0` destroy are not a substitute. The optional download uses
|
||
`GET /api/v2/plans/:id/json-output` on `app.terraform.io` with one redirect to
|
||
`archivist.terraform.io` and does not create, apply, discard, or poll runs.
|
||
|
||
## Migration gates (G6)
|
||
|
||
Provider-real, not reflection-derived. Run against the configured provider:
|
||
|
||
- **Actual-provider discovery** — confirm the runtime provider matches the
|
||
configured connection (e.g., SQL Server / Npgsql) from configuration, not
|
||
from package guesses.
|
||
- **Snapshot consistency** — `dotnet ef migrations script --idempotent` (or a
|
||
model diff) shows no phantom operations; `ModelSnapshot` matches the model.
|
||
- **Empty-db apply** — `dotnet ef database update` against a fresh empty
|
||
database applies the full history cleanly.
|
||
- **Supported-prior upgrade** — upgrade from each still-supported prior
|
||
migration applies cleanly.
|
||
- **Rollback** — report **not guaranteed** unless a real downgrade was
|
||
exercised against the provider. Never claim "EF can roll it back" untested.
|
||
|
||
Migration gates are run on-demand and gated for releases; they are not in the
|
||
per-PR fast path because they require a database. A PR that adds/changes a
|
||
migration must document the G6 evidence in the PR description.
|
||
|
||
## Cancellation forwarding (G7)
|
||
|
||
For changed async I/O paths, the requirement (§6) is **verified forwarding**,
|
||
not a signature checkbox. Acceptable evidence, any one of:
|
||
|
||
- a behavior test asserting the `CancellationToken` reaches the data service /
|
||
EF call; or
|
||
- an analyzer diagnostic confirming propagation on the changed method.
|
||
|
||
A parameter that exists but is swallowed/`default`-ed is a **fail**. If an
|
||
analyzer is unavailable for a path, a behavior test is required; silence is not
|
||
acceptable. Review enforces this on changed paths.
|
||
|
||
## Architecture discovery must fail on zero (G2 invariants)
|
||
|
||
Each reflection invariant in `ArchitectureTests` asserts the discovered set is
|
||
non-empty. A future namespace move that makes reflection return zero types will
|
||
**fail the gate** rather than silently pass. The current invariants:
|
||
|
||
1. Controllers inject neither `DbContext`, concrete data services, nor concrete
|
||
service implementations.
|
||
2. Business services inject neither `DbContext` nor `IConfiguration`.
|
||
3. Business services depend on interfaces (not concrete service/data-service
|
||
implementations) for their data and persistence dependencies.
|
||
|
||
## What this does not claim
|
||
|
||
- Reflection over constructors proves **wiring shape**, not runtime behavior.
|
||
Runtime/semantic properties (cancellation actually cancelling, a query
|
||
executing in SQL, a transaction actually committing atomically) are proven by
|
||
behavior tests or provider evidence, never by reflection.
|
||
- A passing script proves G1–G5 ran in sequence. It does not prove the
|
||
provider-backed or review-owned gates G6–G9.
|
||
|
||
## Pass / fail / skip / not-run reporting
|
||
|
||
Every gate result is reported as exactly one of:
|
||
|
||
- **pass** — ran and succeeded, with evidence.
|
||
- **fail** — ran and failed; cite the failing test/command and output.
|
||
- **skipped** — intentionally not run for this change (e.g., no changed C# for
|
||
G3); name the gate and the reason.
|
||
- **not-run** — could not run (environment/dependency missing); name the
|
||
blocker.
|
||
|
||
A PR is not "green" if any applicable gate is fail, skipped-without-justification,
|
||
or not-run. Never infer a pass from silence.
|
||
|
||
## Adding or changing a gate
|
||
|
||
- Any new executable check goes into `scripts/governance-check.sh` **and** the
|
||
`architecture-quality` workflow together (or into the full CI for G4/G5-type
|
||
checks). One source of truth for local and CI.
|
||
- A new rule documents its meaning in `ARCHITECTURE_AND_CODE_QUALITY.md` and its
|
||
command/mapping here.
|
||
- Explicit, documented exceptions only — no wildcard suppressions (see
|
||
`ARCHITECTURE_AND_CODE_QUALITY.md` §10).
|