Files
clean-architecture-backend-…/.github/ci-gate-matrix.yml
T
DongHyeonkaandClaude Opus 5 40ee9f1e83 fix(build,ci): 정의만 되고 안 돌던 레인 13개를 배선하고 버전 11개를 카탈로그로
감사 remediation 의 마지막 항목 두 개.

## 아무 데서도 안 돌던 레인

등록 태스크 97개 중 어느 CI 경로로도 도달하지 않는 것이 20개였다.
있는 줄 알았는데 안 도는 상태 — 이번에 고친 `*CleanArchitectureTest` 와 같은
종류의 문제다. 각각 판단해서 처리했고, 삭제는 0건이다.

- stage 2(`integration-main.yml`, push:main + 03:00) 에 잡 3개 신설:
  mongo 컨테이너 레인 6개 / messaging 계약 증거 3개 / app-bootstrap integrationTest.
  컨테이너가 필요한 레인은 PR 에 두지 않는다 — PR 예산은 5분이고, 단계를 나눈
  이유가 이것이다
- stage 3(`release.yml`) 에 `grpc-stable-release-gate` 신설
  (inprocess/netty/fault) + `app-image-release` 의 needs 로 연결
- 수동 확정 3개: `grpcPerformanceTest`, `openapiCheckSnapshot`(드리프트 검사는
  이미 stage 1 의 `check` 안에 있고 이 태스크는 승인 지점),
  `sampleOffCompile`(stage 1 `sampleOffTest` 의 진부분집합).
  전용 레지스트리 대신 루트 README 에 적었다 — `verifyReadmeCommands` 가
  거기 적힌 태스크의 실재를 검증하므로, 문서가 곧 검사 대상이 된다
- 게이트 매트릭스 행 11개 신설. 잡↔행 양방향 대조 결과 68개 잡 전부 행이 있고
  행 없는 잡도, 어디서도 안 도는 잡도 없다

측정이 틀린 4건은 배선하지 않았다 — 이미 도달하고 있었다:
`jpaPlatformReleaseGate`(`jpaReleaseGate dependsOn`),
`generateJpaEvidenceManifests`(`verifyJpaCandidateEvidence` 경유),
`messagingCertificationTest`(`verifyMessagingCertificationEvidence` 경유),
`stageDockerJar`(호출자가 Gradle 이 아니라 `release.yml` 의 `docker build`).

## 버전 카탈로그 이관

카탈로그를 우회해 문자열로 박혀 있던 값 11개를 `gradle/libs.versions.toml` 로
옮겼다. plugin 5개는 `[plugins]` + `alias(...)`, 툴 3개는 `libs.versions.*.get()`.

`grpcVersion`/`protobufVersion`/`awsSdkVersion` 은 이관이 불가하다고 넘어온
항목이었으나, `ext.x` 를 접근자로 남기고 값만 카탈로그에서 읽으면 소비 파일 9개와
`ca.grpc-platform-module.gradle:28` 의 `findProperty` 계약이 그대로이고 해석
결과도 동일하다. **lockfile 재생성 0건.**

`commons-lang3` / `netty` 는 BOM 오버라이드라 그대로 둔다 — 오버라이드하는
이유가 주석과 분리되면 값만 남고 근거가 사라진다.

## 검증 (깨끗한 체크아웃, 커밋 전)

`verify-gate-matrix.sh` → 107 gates, 101 verified, drift 0 ·
`verify-gradle-wrapper.sh` PASS · 워크플로 YAML 21개 파싱 OK ·
`gradlew help` · `verifyCleanArchitectureDependencies` · `build-logic test` ·
`:app-bootstrap:test` **1001 tests 실패 0** · `:domain-core:check` ·
`verifyDocumentationContracts` · `verifyDependencyLocks` · `verifyReadmeCommands`.

## 남은 문제

mongo 6레인 · `bootstrap-integration` · messaging 매니페스트 스키마 검증은
CI 에서 한 번도 돈 적이 없다. Docker 가 없으면 실패하도록 설계돼 있으므로
**첫 main push 와 03:00 run 이 빨간 것이 정상 시나리오**다. 로컬에서 Docker
레인을 돌려보지 않았고, `mongo-container-lanes` 의 timeout 90분은 실측이 아니라
추정치다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:46:34 +09:00

1026 lines
40 KiB
YAML

# Every CI control in this checkout: one row per job in .github/workflows, plus the delegated-pending
# rows for controls that are real and that no workflow runs.
#
# The rows are grouped by STAGE, because that is how the workflows are now split. Stage 1 blocks a
# merge, stage 2 reports on main after the merge, stage 3 stops a release. A control's stage is the
# honest form of "does this gate development", and it is what release_blocking below records.
#
# Image signing and provenance attestation are still absent. So is any join between the image build
# in release.yml and the two evidence workflows that answer the same release tag from their own files
# (jpa-release.yml, fileserver-release.yml); that join is registered delegated-pending rather than
# left unstated. .github/scripts/verify-gate-matrix.sh checks every row against the repository and is
# itself the gate-matrix-lint job below; it pins no gate count, so registering a new control is
# adding a row here and nothing else.
#
# Fields:
# release_blocking: true, false, or conditional
# mechanism: gradle-custom-task, gradle-plugin-task, contract-test, workflow-job,
# or delegated-pending
# ref: task, plugin@task, repository-relative test path below src/, or workflow job id
# workflow/job: canonical workflow and job that execute or represent the control
# execution: check (through Gradle check), explicit (named in the job), or job
#
# release_blocking is verified, not declared:
# true a release gate waits on this job — it is ci-quality-gates.yml::release-gate itself,
# one of that job's `needs:`, a name in its REQUIRED_CHECKS, or a job in a workflow
# that only runs on a release tag. The lint fails a `true` row that is none of those.
# conditional the control is real and gates something, but no release gate can require it: it runs
# behind a path filter, a schedule, a manual dispatch, or an input, so its check run
# does not exist for every commit a release gate sees.
# false advisory. A failure here is a signal, not a stop.
gates:
# =================================================================================
# STAGE 1 — pull request. These block a merge.
# =================================================================================
#
# ci-quality-gates.yml — the repository-wide gate. No path filter: it runs on every pull request
# and every push to main, which is what makes the leaf test suites, the architecture dependency
# gate and the Checkstyle ruleset below cover a diff without any per-adapter job repeating them.
- id: format-lint
release_blocking: true
mechanism: gradle-plugin-task
ref: com.diffplug.spotless@spotlessCheck
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: unit-and-contract-tests
release_blocking: true
mechanism: gradle-plugin-task
ref: java@test
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: conditional-transport-qualification
release_blocking: true
mechanism: gradle-custom-task
ref: conditionalTransportQualification
workflow: ci-quality-gates.yml
job: quality-gates
execution: explicit
- id: clean-architecture-dependencies
release_blocking: true
mechanism: gradle-custom-task
ref: verifyCleanArchitectureDependencies
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: environment-contract
release_blocking: true
mechanism: gradle-custom-task
ref: verifyEnvKeys
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: declared-dependency-absence
release_blocking: true
mechanism: gradle-custom-task
ref: verifyDependencyPolicy
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: notification-api-surface
release_blocking: true
mechanism: gradle-custom-task
ref: verifyNotificationApiSurface
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: notification-configuration-contract
release_blocking: true
mechanism: gradle-custom-task
ref: verifyNotificationConfiguration
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: notification-support-grade-evidence
release_blocking: true
mechanism: gradle-custom-task
ref: verifyNotificationEvidence
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: graphql-api-surface
release_blocking: true
mechanism: gradle-custom-task
ref: verifyGraphQlApiSurface
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: mongo-api-surface
release_blocking: true
mechanism: gradle-custom-task
ref: verifyMongoApiSurface
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
#
# The strongest evidence this repository produces, and CI does not run it. Fifteen Compose lanes
# start real PostgreSQL, MongoDB, Kafka, MinIO, Mailpit and Keycloak, take a real client-credentials
# JWT, and prove things no in-JVM test can: that all-off boots with no external resource, that the
# notification handoff delivers exactly once across a restart on the same volume, that the startup
# log is silent. It runs from a developer's machine via scripts/run-compose-runtime-smoke.sh and
# from nowhere else — no workflow invokes it, so nothing re-runs it on a pull request.
#
# Registered delegated-pending so the gap is a tracked absence rather than an unstated one.
# Executing it in CI needs a Docker-capable runner and a decision about the minutes fifteen
# container lanes cost, which is an infrastructure choice rather than a wiring oversight.
- id: runtime-smoke-matrix
release_blocking: conditional
mechanism: delegated-pending
ref: runtime-smoke-matrix-lane
workflow: ci-quality-gates.yml
job: release-gate
execution: job
#
# `conditional-transport-qualification` above is the registered GraphQL control, and it is a
# boundary test: a @SpringBootTest over a nested test application with in-memory Basic Auth. Its own
# javadoc says so — "the nested application deliberately owns only test authentication and CORS
# policy". That is a legitimate transport-boundary proof and it is not release evidence for the
# security posture, which is the distinction the Definition of Done draws.
#
# The real proof exists: the local-graphql Compose lane obtains a Keycloak client-credentials token
# and posts it to /graphql on the running bootJar, asserting that anonymous and malformed
# credentials are refused and the authenticated query answers. It is part of the runtime smoke
# matrix above, so it inherits that control's pending status rather than having none of its own.
- id: graphql-runtime-jwt
release_blocking: conditional
mechanism: delegated-pending
ref: graphql-runtime-jwt-lane
workflow: ci-quality-gates.yml
job: release-gate
execution: job
#
# Checkstyle, not the retired regex. verifyOneTypePerFile parsed Java with
# `^public\s+...` line by line; OneTopLevelClass and OuterTypeFilename ask the same two questions
# against a parsed file and see the package-private top-level types the regex could not. The task
# still exists in src/build.gradle as an aggregate of every leaf's checkstyleMain, but no workflow
# names it any more, so this row names what actually runs: checkstyleMain, inside `check`.
- id: one-type-per-file
release_blocking: true
mechanism: gradle-plugin-task
ref: checkstyle@checkstyleMain
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: trivy-suppression-governance
release_blocking: true
mechanism: gradle-custom-task
ref: verifyTrivyignore
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: quarantine-sunset
release_blocking: true
mechanism: gradle-custom-task
ref: verifyQuarantineSunset
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: public-path-snapshot
release_blocking: true
mechanism: gradle-custom-task
ref: verifyPublicPathSnapshot
workflow: ci-quality-gates.yml
job: quality-gates
execution: explicit
- id: dependency-locks
release_blocking: true
mechanism: gradle-custom-task
ref: verifyDependencyLocks
workflow: ci-quality-gates.yml
job: quality-gates
execution: explicit
#
# Points at the inventory guard rather than at one suite. The 87 architecture rules used to be
# 74-in-one-class plus a scattered remainder, so naming CleanArchitectureTest.java named most of
# them and silently omitted the rest; after the split (BOOT-012) it would have named a fifth of
# them. ArchRuleDiscoveryContractTest pins every rule in the package by name and fails if one is
# declared where ArchUnit cannot discover it, which is the property this gate actually exists to
# protect.
- id: architecture-contract-test
release_blocking: true
mechanism: contract-test
ref: app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
- id: sample-off
release_blocking: true
mechanism: gradle-custom-task
ref: sampleOffTest
workflow: ci-quality-gates.yml
job: sample-off
execution: explicit
- id: gate-matrix-lint
release_blocking: true
mechanism: workflow-job
ref: gate-matrix-lint
workflow: ci-quality-gates.yml
job: gate-matrix-lint
execution: job
- id: redis-sdk
release_blocking: true
mechanism: workflow-job
ref: redis-sdk
workflow: ci-quality-gates.yml
job: redis-sdk
execution: job
- id: jpa-candidate-evidence
release_blocking: true
mechanism: workflow-job
ref: jpa-candidate-evidence
workflow: ci-quality-gates.yml
job: jpa-candidate-evidence
execution: job
- id: quality-release-gate
release_blocking: true
mechanism: workflow-job
ref: release-gate
workflow: ci-quality-gates.yml
job: release-gate
execution: job
- id: flaky-quarantine
release_blocking: false
mechanism: workflow-job
ref: quarantine
workflow: ci-quality-gates.yml
job: quarantine
execution: job
- id: redis-sdk-support-matrix
release_blocking: true
mechanism: contract-test
ref: adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/RedisSupportMatrixTest.java
workflow: ci-quality-gates.yml
job: quality-gates
execution: check
#
# pr-adapters.yml — the lanes `check` cannot reach: a second servlet container, a real Nginx,
# Reactor Netty, one PostgreSQL major per job, one HTTP transport per job. Each job is behind a
# per-job path filter computed from the pull request's diff, so no release gate can wait on one.
- id: httpclient-contract-stable-contract
release_blocking: conditional
mechanism: workflow-job
ref: httpclient-stable-contract
workflow: pr-adapters.yml
job: httpclient-stable-contract
execution: job
- id: httpclient-security-and-compatibility
release_blocking: conditional
mechanism: workflow-job
ref: httpclient-security-and-compatibility
workflow: pr-adapters.yml
job: httpclient-security-and-compatibility
execution: job
- id: jpa-postgresql-contract
release_blocking: conditional
mechanism: workflow-job
ref: jpa-postgresql-contract
workflow: pr-adapters.yml
job: jpa-postgresql-contract
execution: job
- id: jpa-migration-smoke
release_blocking: conditional
mechanism: workflow-job
ref: jpa-migration-smoke
workflow: pr-adapters.yml
job: jpa-migration-smoke
execution: job
- id: web-cross-stack-parity
release_blocking: conditional
mechanism: workflow-job
ref: web-cross-stack-parity
workflow: pr-adapters.yml
job: web-cross-stack-parity
execution: job
- id: web-nginx-proxy-contract
release_blocking: conditional
mechanism: workflow-job
ref: web-nginx-proxy-contract
workflow: pr-adapters.yml
job: web-nginx-proxy-contract
execution: job
- id: websocket-container-matrix
release_blocking: conditional
mechanism: workflow-job
ref: websocket-container-matrix
workflow: pr-adapters.yml
job: websocket-container-matrix
execution: job
- id: websocket-nginx-contract
release_blocking: conditional
mechanism: workflow-job
ref: websocket-nginx-contract
workflow: pr-adapters.yml
job: websocket-nginx-contract
execution: job
#
# The job every lane in that file reads. It fails closed on an unreadable diff rather than
# reporting that nothing changed, because a filter that answers false on a broken comparison
# turns off every gate behind it and reports green.
- id: pr-adapter-change-filter
release_blocking: conditional
mechanism: workflow-job
ref: changes
workflow: pr-adapters.yml
job: changes
execution: job
#
# fileserver-pr.yml — path-filtered pull-request gates. They stay in their own file because
# FileserverDocumentationCoverageTest requires every job id docs/fileserver/support-matrix.md
# names to be defined in a .github/workflows/fileserver-*.yml.
#
# Path-filtered pull-request gates. They are the only automated check of the fileserver's
# ext4 and HTTP contracts, but they start only when the filtered paths change, so no release
# gate can wait on them.
- id: fileserver-unit-and-architecture
release_blocking: conditional
mechanism: workflow-job
ref: fileserver-unit-and-architecture
workflow: fileserver-pr.yml
job: fileserver-unit-and-architecture
execution: job
- id: fileserver-local-ext4-contract
release_blocking: conditional
mechanism: workflow-job
ref: fileserver-local-ext4-contract
workflow: fileserver-pr.yml
job: fileserver-local-ext4-contract
execution: job
- id: fileserver-http-contract
release_blocking: conditional
mechanism: workflow-job
ref: fileserver-http-contract
workflow: fileserver-pr.yml
job: fileserver-http-contract
execution: job
- id: fileserver-security-suite
release_blocking: conditional
mechanism: workflow-job
ref: fileserver-security-suite
workflow: fileserver-pr.yml
job: fileserver-security-suite
execution: job
- id: fileserver-bounded-memory
release_blocking: conditional
mechanism: workflow-job
ref: fileserver-bounded-memory
workflow: fileserver-pr.yml
job: fileserver-bounded-memory
execution: job
#
# dependency-vulnerability.yml — supply chain. trivy-fs is the one cross-workflow check
# ci-quality-gates.yml::release-gate requires by name through REQUIRED_CHECKS.
- id: dependency-review
release_blocking: conditional
mechanism: workflow-job
ref: dependency-review
workflow: dependency-vulnerability.yml
job: dependency-review
execution: job
- id: dependency-submission
release_blocking: false
mechanism: workflow-job
ref: dependency-submission
workflow: dependency-vulnerability.yml
job: dependency-submission
execution: job
- id: filesystem-vulnerability-scan
release_blocking: true
mechanism: workflow-job
ref: trivy-fs
workflow: dependency-vulnerability.yml
job: trivy-fs
execution: job
#
# link-check.yml — committed documentation links, path-filtered.
- id: documentation-links
release_blocking: conditional
mechanism: workflow-job
ref: lychee
workflow: link-check.yml
job: lychee
execution: job
# =================================================================================
# STAGE 2 — merged state. These do not block a merge; the merge already happened.
# =================================================================================
#
# integration-main.yml — push to main, nightly, or dispatch. The documentation-drift gates that
# left `check`, and the lanes that need a machine which is not simultaneously compiling.
- id: documented-leaf-count
release_blocking: false
mechanism: gradle-custom-task
ref: verifyDocumentedLeafCount
workflow: integration-main.yml
job: documentation-contracts
execution: job
- id: runbook-reference-drift
release_blocking: false
mechanism: gradle-custom-task
ref: verifyRunbookReferences
workflow: integration-main.yml
job: documentation-contracts
execution: job
- id: readme-command-drift
release_blocking: false
mechanism: gradle-custom-task
ref: verifyReadmeCommands
workflow: integration-main.yml
job: documentation-contracts
execution: job
#
# Scheduled. httpclient-nightly-http3-experimental is continue-on-error on purpose: HTTP/3 is
# an opt-in experiment (-Phttp3.tests.enabled) and a red experiment must not bury a real
# nightly regression. Registered false so the matrix says that out loud instead of leaving a
# job that cannot fail unrecorded.
- id: httpclient-nightly-fault-injection
release_blocking: false
mechanism: workflow-job
ref: httpclient-fault-injection
workflow: integration-main.yml
job: httpclient-fault-injection
execution: job
- id: httpclient-nightly-performance
release_blocking: false
mechanism: workflow-job
ref: httpclient-performance
workflow: integration-main.yml
job: httpclient-performance
execution: job
- id: httpclient-nightly-http3-experimental
release_blocking: false
mechanism: workflow-job
ref: httpclient-http3-experimental
workflow: integration-main.yml
job: httpclient-http3-experimental
execution: job
#
# Scheduled load, abuse and shutdown lane.
- id: web-load-abuse-and-shutdown
release_blocking: false
mechanism: workflow-job
ref: web-load-abuse-and-shutdown
workflow: integration-main.yml
job: web-load-abuse-and-shutdown
execution: job
#
# The four documentation-drift gates, as one task. They were `dependsOn` of the root `check`
# and are not any more: a README sentence about a renamed task should not fail a
# compile-and-test run. This job is what keeps that a demotion rather than a deletion — with
# no workflow invoking the aggregate, all four would run nowhere. release_blocking: false is
# the demotion stated as a fact the lint can check.
- id: documentation-contracts
release_blocking: false
mechanism: gradle-custom-task
ref: verifyDocumentationContracts
workflow: integration-main.yml
job: documentation-contracts
execution: explicit
#
# Ran inside verifyDocumentationContracts above rather than named in the job, which is why
# this row is execution: job. It had no row at all before.
- id: test-source-set-registry
release_blocking: false
mechanism: gradle-custom-task
ref: verifyTestSourceSetRegistry
workflow: integration-main.yml
job: documentation-contracts
execution: job
#
# The six Docker-backed MongoDB lanes. Registered in the leaf, excluded from `test` by tag, absent
# from `check`, and named by no workflow until now — scripts/verify-mongodb-platform.sh was the
# only caller and nothing in .github runs it. One row per lane because each is its own single-line
# command, which is what makes `execution: explicit` checkable.
#
# release_blocking: false is the honest value, not a demotion: stage 2 runs after the merge, so no
# release gate can wait on it. Promoting these to stage 1 would put a replica set, a three-node
# failover and a Toxiproxy on the pull-request budget.
- id: mongo-replica-set-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoReplicaSetTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
- id: mongo-failover-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoFailoverTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
- id: mongo-migration-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoMigrationTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
- id: mongo-compatibility-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoCompatibilityTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
- id: mongo-security-integration-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoSecurityIntegrationTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
- id: mongo-performance-lane
release_blocking: false
mechanism: gradle-custom-task
ref: mongoPerformanceTest
workflow: integration-main.yml
job: mongo-container-lanes
execution: explicit
#
# The messaging contract evidence DAG. One row, because one job is one control and the control is
# the aggregate: `verifyMessagingContracts` reaches five strict qualification tasks, and through
# them `prepareMessagingContractEvidence`, plus both manifest schema validators
# (validateMessagingJsonSchemaV1EvidenceManifestSchema by dependsOn,
# validateMessagingContractsEvidenceManifestSchema by finalizedBy). Strict qualification tasks are
# registered outside `check` by design, so before this job none of the seven executed anywhere.
- id: messaging-contract-evidence
release_blocking: false
mechanism: gradle-custom-task
ref: verifyMessagingContracts
workflow: integration-main.yml
job: messaging-contract-evidence
execution: explicit
#
# app-bootstrap's Testcontainers lane. Its own source set exists so that `:app-bootstrap:test`
# needs no Docker daemon; the unclosed consequence was that a source set outside `test` is also
# outside `check`, so these contracts compiled everywhere and ran nowhere.
- id: bootstrap-integration-contracts
release_blocking: false
mechanism: gradle-custom-task
ref: integrationTest
workflow: integration-main.yml
job: bootstrap-integration
execution: explicit
#
# notification-platform.yml — `pr` is path-filtered and also runs on push to main; nightly-chaos
# is schedule/dispatch only. The release-blocking notification controls are the verifyNotification*
# rows above, which run inside check on every pull request.
#
# `pr` is path-filtered (and runs on push to main); nightly-chaos is schedule/dispatch only.
# The release-blocking notification controls are the verifyNotification* rows above, which run
# inside check on every pull request.
- id: notification-platform-pr
release_blocking: conditional
mechanism: workflow-job
ref: pr
workflow: notification-platform.yml
job: pr
execution: job
- id: notification-platform-nightly-chaos
release_blocking: false
mechanism: workflow-job
ref: nightly-chaos
workflow: notification-platform.yml
job: nightly-chaos
execution: job
#
# messaging-certification.yml — path-filtered pull request plus a weekly schedule.
#
# The messaging platform's only claim that needs a real broker to be true. The gate is the
# evidence check rather than the lane, and it depends on the lane: passing means both that every
# fault scenario produced the outcome the shared contract fixes and that the committed manifest is
# what this run wrote. Before it existed, `CertifiedEvidence` was a hand-authored list and
# "certified against a live broker" was a sentence a developer could type.
- id: messaging-broker-certification
release_blocking: conditional
mechanism: gradle-custom-task
ref: verifyMessagingCertificationEvidence
workflow: messaging-certification.yml
job: broker-certification
execution: explicit
#
# object-storage-qualification.yml — pull request, weekly schedule, and two protected dispatch
# inputs for the AWS sandbox lane.
#
# These three ran under `release_blocking: true` while nothing waited on them. object-storage-
# qualification.yml has no push-to-main trigger and messaging-certification.yml is behind a path
# filter, so neither produces a check run for every commit ci-quality-gates.yml::release-gate
# judges; requiring them there would hang on the commits where they never start. They are
# conditional — real controls on their own trigger — until someone decides to widen that trigger,
# which is a CI-minutes decision about MinIO and Kafka containers, not a wiring oversight.
- id: object-storage-minio-managed-contract
release_blocking: conditional
mechanism: gradle-custom-task
ref: objectStorageMinioContractTest
workflow: object-storage-qualification.yml
job: minio-managed-contract
execution: explicit
- id: poster-image-migration
release_blocking: conditional
mechanism: gradle-custom-task
ref: posterImageMigrationTest
workflow: object-storage-qualification.yml
job: poster-image-v7-migration
execution: explicit
- id: object-storage-minio-managed-fault
release_blocking: conditional
mechanism: gradle-custom-task
ref: objectStorageMinioFaultTest
workflow: object-storage-qualification.yml
job: minio-managed-fault
execution: explicit
- id: object-storage-aws-protected-qualification
release_blocking: conditional
mechanism: delegated-pending
ref: approval-gate-b
workflow: object-storage-qualification.yml
job: aws-managed-common-subset
execution: job
#
# redis-sdk-topology.yml — path-filtered pull request (standalone only) and the nightly matrix.
#
# Promoted from delegated-pending: the workflow is no longer manual-only. A pull request that
# touches the Redis leaf runs the standalone lane, and the full supported-version x topology
# matrix runs nightly and on a release candidate. While it was dispatch-only, a release could
# claim topology evidence that nobody had produced for that commit.
- id: redis-sdk-topology-evidence
release_blocking: conditional
mechanism: workflow-job
ref: topology-evidence
workflow: redis-sdk-topology.yml
job: topology-evidence
execution: job
#
# The lane that topology-evidence needs. Same trigger as the evidence row above it.
- id: redis-sdk-topology-lanes
release_blocking: conditional
mechanism: workflow-job
ref: lanes
workflow: redis-sdk-topology.yml
job: lanes
execution: job
#
# fileserver-nightly.yml — scheduled only. NFS ambiguity, process-kill and lease behaviour need
# hours and real filesystems; a failure is a signal to act on, not a stop on a release that did not
# cause it.
#
# Scheduled only. NFS ambiguity, process-kill and lease behaviour need hours and real
# filesystems; a failure is a signal to act on, not a stop on a release that did not cause it.
- id: fileserver-nfs-ambiguity
release_blocking: false
mechanism: workflow-job
ref: fileserver-nfs-ambiguity
workflow: fileserver-nightly.yml
job: fileserver-nfs-ambiguity
execution: job
- id: fileserver-process-kill-matrix
release_blocking: false
mechanism: workflow-job
ref: fileserver-process-kill-matrix
workflow: fileserver-nightly.yml
job: fileserver-process-kill-matrix
execution: job
- id: fileserver-large-file-performance
release_blocking: false
mechanism: workflow-job
ref: fileserver-large-file-performance
workflow: fileserver-nightly.yml
job: fileserver-large-file-performance
execution: job
- id: fileserver-multi-instance-lease
release_blocking: false
mechanism: workflow-job
ref: fileserver-multi-instance-lease
workflow: fileserver-nightly.yml
job: fileserver-multi-instance-lease
execution: job
#
# jpa-nightly.yml — the middle of the PostgreSQL matrix and the suites too slow or too
# Docker-heavy for a pull request.
#
# Scheduled JPA matrix, failure injection, query-plan/security and pool pressure.
- id: jpa-full-matrix
release_blocking: false
mechanism: workflow-job
ref: jpa-full-matrix
workflow: jpa-nightly.yml
job: jpa-full-matrix
execution: job
- id: jpa-failure-injection
release_blocking: false
mechanism: workflow-job
ref: jpa-failure-injection
workflow: jpa-nightly.yml
job: jpa-failure-injection
execution: job
- id: jpa-query-plan-and-security
release_blocking: false
mechanism: workflow-job
ref: jpa-query-plan-and-security
workflow: jpa-nightly.yml
job: jpa-query-plan-and-security
execution: job
- id: jpa-pool-pressure
release_blocking: false
mechanism: workflow-job
ref: jpa-pool-pressure
workflow: jpa-nightly.yml
job: jpa-pool-pressure
execution: job
#
# jpa-next-*.yml — weekly early-warning lanes against unreleased upstream versions. They exist to
# find out before the upgrade, so they block nothing.
#
# Weekly early-warning lanes against unreleased upstream versions. They exist to find out
# before the upgrade, so they block nothing.
- id: hibernate8-compatibility
release_blocking: false
mechanism: workflow-job
ref: hibernate8-compatibility
workflow: jpa-next-hibernate8.yml
job: hibernate8-compatibility
execution: job
- id: jpa4-compatibility
release_blocking: false
mechanism: workflow-job
ref: jpa4-compatibility
workflow: jpa-next-jpa4.yml
job: jpa4-compatibility
execution: job
- id: postgresql19-compatibility
release_blocking: false
mechanism: workflow-job
ref: postgresql19-compatibility
workflow: jpa-next-postgresql19.yml
job: postgresql19-compatibility
execution: job
#
# jpa-r2-evidence.yml — dispatch-only production-profile manifest run.
- id: jpa-r2-evidence
release_blocking: conditional
mechanism: workflow-job
ref: jpa-r2-evidence
workflow: jpa-r2-evidence.yml
job: jpa-r2-evidence
execution: job
# =================================================================================
# STAGE 3 — release tag. These stop a release.
# =================================================================================
#
# release.yml — one workflow, one deployable unit. Evidence jobs first, then app-image-release,
# which `needs:` all of them: while the image build lived in its own workflow it could publish
# while a sibling suite was still running or already red.
- id: httpclient-stable-contract
release_blocking: true
mechanism: gradle-custom-task
ref: httpClientStableContractTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
- id: httpclient-security-suite
release_blocking: true
mechanism: gradle-custom-task
ref: httpClientSecurityTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
- id: httpclient-fault-injection
release_blocking: true
mechanism: gradle-custom-task
ref: httpClientFailureInjectionTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
- id: httpclient-performance-certification
release_blocking: true
mechanism: gradle-custom-task
ref: httpClientPerformanceTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
- id: httpclient-spring62-api-surface
release_blocking: true
mechanism: gradle-custom-task
ref: spring62ApiSurfaceScan
workflow: release.yml
job: httpclient-release-gate
execution: explicit
#
# The 6.2 API-surface scan above proves the common packages compile against the older surface. It
# does not prove they run on it, and the two were being conflated: a lane called
# "spring62CompatibilityTest" reads as a runtime compatibility proof. The Gradle task is renamed to
# say what it does, and the runtime claim is registered here as its own delegated-pending control
# so the gap is a tracked absence rather than an unstated one. Executing it needs a Spring
# Framework 6.2 distribution resolved into a separate test runtime, which this repository's
# Boot 4.0 baseline does not carry.
- id: httpclient-spring62-runtime
release_blocking: conditional
mechanism: delegated-pending
ref: spring62-runtime-lane
workflow: release.yml
job: httpclient-release-gate
execution: job
- id: httpclient-spring70-compatibility
release_blocking: true
mechanism: gradle-custom-task
ref: spring70CompatibilityTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
- id: httpclient-documentation-drift
release_blocking: true
mechanism: workflow-job
ref: httpclient-documentation
workflow: release.yml
job: httpclient-documentation
execution: job
- id: httpclient-event-loop-blocking
release_blocking: true
mechanism: gradle-custom-task
ref: httpClientBlockHoundTest
workflow: release.yml
job: httpclient-release-gate
execution: explicit
#
# Tag-triggered (`v*`). The Stable web release gate.
- id: web-stable-release-gate
release_blocking: true
mechanism: workflow-job
ref: web-stable-release-gate
workflow: release.yml
job: web-stable-release-gate
execution: job
#
# Tag-triggered (`v*`). The Stable websocket release gate.
- id: websocket-stable-release-gate
release_blocking: true
mechanism: workflow-job
ref: websocket-stable-release-gate
workflow: release.yml
job: websocket-stable-release-gate
execution: job
#
# Tag-triggered (`v*`). The three gRPC certification lanes, one row each because each runs as its
# own single-line command and `execution: explicit` is only verifiable that way.
#
# These were registered lanes that no workflow named. Their tests are not new coverage — the
# `grpc-inprocess`, `grpc-netty` and `grpc-fault` tags run inside `:grpc:grpc-testkit:test`, which
# is inside `check` — so what the lanes add is the fail-on-nothing-discovered guard that a tag
# filter inside `test` cannot give you. Same argument as web-advanced and websocket-advanced above,
# and the same placement.
- id: grpc-inprocess-contract-lane
release_blocking: true
mechanism: gradle-custom-task
ref: grpcInProcessContractTest
workflow: release.yml
job: grpc-stable-release-gate
execution: explicit
- id: grpc-netty-transport-lane
release_blocking: true
mechanism: gradle-custom-task
ref: grpcNettyContractTest
workflow: release.yml
job: grpc-stable-release-gate
execution: explicit
- id: grpc-fault-lane
release_blocking: true
mechanism: gradle-custom-task
ref: grpcFaultTest
workflow: release.yml
job: grpc-stable-release-gate
execution: explicit
#
# Tag-triggered (`v*`). The only job in this repository that produces a deployable artifact.
#
# One row, not three, because one job is one control: the job builds the app-bootstrap image,
# generates its CycloneDX SBOM, refuses to publish on a CRITICAL or HIGH finding, and only then
# pushes the semver and sha- tags and records the digest. The scan is a step inside that control
# rather than a control of its own, which is the same shape filesystem-vulnerability-scan uses for
# the trivy-fs job. `release_blocking: true` holds because the workflow runs only for a release
# tag, so the job failing fails that release run.
- id: container-image-release
release_blocking: true
mechanism: workflow-job
ref: app-image-release
workflow: release.yml
job: app-image-release
execution: job
#
# The join this repository cannot express, stated rather than implied.
#
# Most of it is closed now. app-image-release lives in release.yml and `needs:` the four evidence
# jobs in that file, so the image cannot be built, scanned or pushed past a failed web, websocket,
# httpclient or architecture gate. `needs:` still reaches only inside one workflow file, and two
# evidence workflows are still outside it: jpa-release.yml and fileserver-release.yml both answer
# the same `v*` tag and neither can be waited on from here.
#
# Those two are not in release.yml for a mechanical reason rather than a design one —
# JpaReleaseRenderingTest reads `.github/workflows/jpa-release.yml` by that exact path, and
# FileserverDocumentationCoverageTest requires the job ids docs/fileserver/support-matrix.md names
# to be defined in a `.github/workflows/fileserver-*.yml`. Folding them in means changing a test
# and a document in src/ and docs/ in the same commit, which is a decision with owners rather than
# a wiring oversight.
#
# Until then the remaining control is a human one: the GitOps repository promotes a digest whose
# tag's jpa-release and fileserver-release runs somebody has looked at, not a digest that merely
# exists. Registered here so that sentence lives somewhere a lint can point at, the way
# fileserver-pvc-cluster-certification does for the storage claim.
- id: container-release-evidence-join
release_blocking: conditional
mechanism: delegated-pending
ref: container-release-evidence-join
workflow: release.yml
job: app-image-release
execution: job
#
# verifyCleanArchitectureDependencies, verifyPublicPathSnapshot, verifyEnvKeys and the
# bootstrap architecture suite, once per release. The four release workflows this replaced ran
# the dependency gate six times and the architecture suite four times for one tag, on separate
# runners, against one commit.
- id: release-architecture-and-surface
release_blocking: true
mechanism: workflow-job
ref: architecture-and-surface
workflow: release.yml
job: architecture-and-surface
execution: job
#
# jpa-release.yml — tag-triggered (`v*`). jpa-release-promotion needs jpa-release-gate, so the
# promotion cannot run past a failed gate. Its own file because JpaReleaseRenderingTest reads that
# exact path and holds its matrix to src/config/jpa/release-registry.json.
#
# Tag-triggered (`v*`). jpa-release-promotion needs jpa-release-gate, so the promotion cannot
# run past a failed gate.
- id: jpa-release-gate
release_blocking: true
mechanism: workflow-job
ref: jpa-release-gate
workflow: jpa-release.yml
job: jpa-release-gate
execution: job
- id: jpa-release-promotion
release_blocking: true
mechanism: workflow-job
ref: jpa-release-promotion
workflow: jpa-release.yml
job: jpa-release-promotion
execution: job
- id: jpa-architecture-and-docs
release_blocking: true
mechanism: workflow-job
ref: jpa-architecture-and-docs
workflow: jpa-release.yml
job: jpa-architecture-and-docs
execution: job
#
# fileserver-release.yml — tag-triggered (`v*`). Its own file for the same reason fileserver-pr.yml
# is.
#
# Tag-triggered: `v*` runs this workflow and a failing job fails that
# release. fileserver-pvc-certification checks the manifest only — the cluster half is the
# delegated-pending row below it.
- id: fileserver-full-verification
release_blocking: true
mechanism: workflow-job
ref: fileserver-full-verification
workflow: fileserver-release.yml
job: fileserver-full-verification
execution: job
- id: fileserver-documentation-gate
release_blocking: true
mechanism: workflow-job
ref: fileserver-documentation-gate
workflow: fileserver-release.yml
job: fileserver-documentation-gate
execution: job
- id: fileserver-pvc-certification
release_blocking: true
mechanism: workflow-job
ref: fileserver-pvc-certification
workflow: fileserver-release.yml
job: fileserver-pvc-certification
execution: job
#
# The other half of the PVC claim, and the half no runner can produce. An operator
# applies infra/fileserver/kubernetes/pvc-certification-job.yaml to a real cluster and
# records the result in docs/fileserver/storage-certification.md. The workflow step
# that pretended to do this in CI reported success whenever the cluster secret was
# absent, which is every checkout of this template; it is gone, and the claim is
# tracked here instead of being green for nothing.
- id: fileserver-pvc-cluster-certification
release_blocking: conditional
mechanism: delegated-pending
ref: fileserver-pvc-cluster-lane
workflow: fileserver-release.yml
job: fileserver-pvc-certification
execution: job
- id: fileserver-sensitive-telemetry-scan
release_blocking: true
mechanism: workflow-job
ref: fileserver-sensitive-telemetry-scan
workflow: fileserver-release.yml
job: fileserver-sensitive-telemetry-scan
execution: job