43 KiB
title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
| title | source_type | status | branch | parent_branch | related_projects | governing_docs | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | contract_packet_sha256 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-ci-quality-gates-contract | branch-note | raw | feature-ci-quality-gates-contract |
|
|
|
2026-05-22 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-028 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-028 |
|
1 | e13ec9fd666d546ce8cb089f48f43da5ed3d77a72d0c6edc691bad11e65956e8 |
branch: feature-ci-quality-gates-contract
Layer:
raw/branch-notes/— skeleton 계약이 문서에만 남지 않도록 CI에서 강제할 quality gate 기준을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다.
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: architecture·contract·OpenAPI blocking gate가 분리 실행된다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1 |
verification suite가 OpenAPI drift의 release-blocking 판정권을 소유한다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1 |
test framework는 JUnit 5다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
목표
운영 계약은 깨지기 쉽습니다. response envelope, log schema, env fail-fast, OpenAPI drift, repository capability, security/log leakage 같은 항목은 CI에서 실패 조건으로 고정해야 합니다.
본 branch 의 책임은 gate wiring(어떤 gate 가 CI 에서 어떻게 실행/차단되는가) 이다. 개별 scanner/tool/severity 정책 결정 은 전용 owner branch 가 소유하고 본 branch 는 그 결과를 release-blocking gate 로 배선 한다 (§구현 가이드 §6, §엣지·실패·의존 의존 목록).
- 이슈:
- PR:
범위
포함 범위
- format/lint/test/contract test gate.
- OpenAPI drift check.
- dependency vulnerability scan gate.
- optional adapter test matrix.
- warning-only와 release-blocking gate 구분.
제외 범위
- 실제 CI provider workflow 구현 세부.
- 배포 승인 프로세스.
- load/performance test.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/ci-github-actions-vs-gitlab-comparison | GitHub Actions needs: + `if: success( |
| raw/official-docs/ci-openapi-snapshot-diff-tooling | springdoc + openapi-diff (Tufin/oasdiff |
| raw/company-tech-blogs/ci-flaky-test-quarantine-spotify-google | Spotify/Google/MS quarantine 인정 vs Fowler 반대 |
외부 근거 / 대안 조사 (2026-05-22 — Group G-E: CI Quality Gates)
본 branch의 Gate ownership matrix 20행 + flaky quarantine 14d + OpenAPI snapshot diff 결정에 대한 외부 source.
- 채택 결정 (GitHub Actions + matrix gate + flaky 14d sunset):
- raw/official-docs/ci-github-actions-vs-gitlab-comparison — GitHub Actions
needs:+if: success()가 ca-tmpl contract gate 모델과 정확히 맞물림 - raw/official-docs/ci-openapi-snapshot-diff-tooling — springdoc + openapi-diff (Tufin/oasdiff)
- raw/official-docs/ci-github-actions-vs-gitlab-comparison — GitHub Actions
- 검토한 대안:
- 대안 1: GitLab CI vs GitHub Actions — 동일 source에서 비교. 선택 조건: ca-tmpl repo 가 GitHub 호스팅 → GitHub Actions 채택; GitLab 호스팅으로 이전 시
needs↔stages매핑(CIGG-C2)으로 이식 가능 (provider 선정 자체는 별도 ADR —CIGG-C2는 매핑 가능성 만 보장) - 대안 2: Jenkins / Tekton (k8s-native) — k8s 인프라/plugin 의존도로 skeleton 단계에 과함
- 대안 3: CircleCI / Buildkite / Drone CI — vendor 다양성, ca-tmpl scope 외
- 사례 (flaky quarantine): raw/company-tech-blogs/ci-flaky-test-quarantine-spotify-google — Spotify/Google/MS quarantine 인정 vs Fowler 반대. ca-tmpl 14일 sunset은 절충안
- 대안 1: GitLab CI vs GitHub Actions — 동일 source에서 비교. 선택 조건: ca-tmpl repo 가 GitHub 호스팅 → GitHub Actions 채택; GitLab 호스팅으로 이전 시
- 비교 핵심: GitHub Actions의
needs:/if:gate 의존성 모델이 ca-tmpl 11 release-blocking gate에 정확히 맞물림. Tekton/Jenkins는 k8s 인프라/plugin 부담으로 skeleton에 과함. Flaky quarantine은 Spotify/Google/MS 인정 vs Fowler 반대 양립 — ca-tmpl 14일 sunset이 절충.
TODO
TODO drained 2026-05-22 — required CI gate 목록 / release-blocking vs warning-only 기준 / contract test 차단 / optional adapter matrix / OpenAPI drift / vulnerability 차단 정책 모두 "결정 사항"과 "Gate ↔ Branch Contract Test 소유권 매트릭스"에 반영됨. 잔존 TODO 없음.
진행 중 메모
- 2026-06-15 (
/branch-spec): pre-template 노트를 현 템플릿 구조로 보강 — 누락 섹션(구현 가이드 / 엣지·실패·의존 / 진행 중 메모 / 관련 일일 노트) 추가,parent_branch+governing_docsfrontmatter 추가, §Coverage seed, §Audit & Findings(ground-truth drift) 추가. 기존 결정·매트릭스·테스트 계약 본문은 verbatim 보존. ca-tmpl ground truth 대조 결과 모든 gate 는 여전히documented-only(.github/workflows/부재 확인) — actually-implemented 주장 없음. - 2026-06-20 (구현): gate wiring 을
actually-implemented(로컬locally-verified)로 승급. 산출물 —.github/workflows/ci-quality-gates.yml(9 잡: quality-gates/security-snapshot-gates/openapi-drift/sample-removal-smoke/optional-adapter-matrix/gate-matrix-lint/breaking-change-approval/quarantine/release-gate fan-in),.github/ci-gate-matrix.yml(20행 in-repo SSOT),.github/scripts/verify-gate-matrix.sh(C7 cross-check),flaky-quarantine.yaml(repo-루트 레지스트리, 빈 버킷),src/build.gradle(testexcludeTags 'quarantine' +quarantineTest버킷 +verifyQuarantineSunset14일 sunset, check 연결),.github/CODEOWNERS/.github/pull_request_template.md(D8 escape-hatch 거버넌스),src/README.md문서.- 핵심 구현 결정 (UNSUPPORTED_IMPL_DECISION 해소):
- §4 quarantine 메커니즘 =
@Tag("quarantine")(JUnit 기본, 전 모듈 즉시 사용) + repo-루트flaky-quarantine.yaml레지스트리 — note 의@QuarantinedSincecustom annotation 후보 대신 채택. 이유: custom annotation 의 cross-module 사용은 test-fixtures/신규 모듈 plumbing 필요(과함)이고,shared-contract(stdlib-only)에 JUnit 타입을 둘 수 없음. 레지스트리 방식은 기존 4개 거버넌스 게이트(verifyTrivyignore/verifyEnvKeys/verifyOneTypePerFile/verifyPublicPathSnapshot)와 동형이며 gitignored-docs 제약(아래)도 회피. - release-gate fan-in =
if: always()+needs.*.result스캔 —if: success()단독은 상위 실패 시 aggregator 가 skipped(차단 아님). Claim C1 의 실증적 해소. - gitignored 설정 제약 발견:
.gitignore가/docs전체 제외(0 tracked) → CI-read 신규 파일은docs/금지, tracked 경로(repo 루트/.github/)에 배치(.trivyignore.yaml선례).check의 registry 의존은 워크플로 "RUNTIME-CONFIG PREREQUISITE" 로 문서화(범위 밖 — env-driven 소유).
- §4 quarantine 메커니즘 =
- 검증(로컬): verifyQuarantineSunset 3종 control(empty→OK / over-age 170d→fail / drift unregistered→fail),
:shared-contract:quarantineTestBUILD SUCCESSFUL(빈 버킷), gate-matrix-lint PASS(20=16 verified+4 delegated), openapiCheckSnapshot/SampleRemovalSmoke/TestTaxonomyArchitectureTest/verifyCleanArchitectureDependencies 통과, 워크플로 YAML PyYAML 파싱 OK. CI 실제 실행은needs-confirmation. - 파생 노트: raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20, raw/blog-topics/ci-gate-wiring-vs-policy-ownership-2026-06-20, raw/interviews/ci-release-gate-fan-in-blocking-2026-06-20.
- 핵심 구현 결정 (UNSUPPORTED_IMPL_DECISION 해소):
Work Item Contract
각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 기준 작성으로 남아 있으면 이 branch는 완료로 보지 않습니다.
| field | required | rule |
|---|---|---|
| Decision | yes | 구현자가 선택해야 하는 기본값 |
| Allowed | yes | 허용되는 예외와 조건 |
| Forbidden | yes | 절대 금지되는 구현/문서 상태 |
| Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 |
| Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 |
| Failure condition | yes | review/build에서 실패로 판정할 상태 |
| Canonical extraction target | yes | wiki/projects 승급 위치 |
결정 사항
- 2026-05-22: contract violation은 warning-only로 두지 않음.
- 2026-05-22: optional adapter test는 adapter enabled matrix에서만 실행.
- 2026-05-22: OpenAPI drift, registry drift, security/privacy leakage, architecture boundary, sample removal smoke는 release-blocking.
- 2026-05-22: warning-only는 dependency freshness advisory처럼 release artifact correctness를 직접 깨지 않는 항목에만 허용.
- 2026-05-22: vulnerability scanner = Trivy (image + dependency). suppression은
trivy-ignore파일 + PR review approval 필수. - 2026-05-22: OpenAPI drift ground truth = code-generated snapshot (springdoc 등). hand-maintained는 forbidden.
- 2026-05-22: flaky test quarantine bucket 허용. quarantine된 test는 별도 gradle task로 분리, sunset deadline 14일.
- 2026-05-22: contract test snapshot 의도적 갱신 = breaking change catalog row 인용 + PR label
intent:breaking-change-approved로 escape hatch. - 2026-05-22: 본 branch가 flaky test quarantine SSOT (sunset 14일). test-taxonomy-fixture-contract는 consumer (flaky 발생 시 quarantine bucket 참조).
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시.
Decision ID는 이 branch-note 안에서 안정적으로 유지.
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | contract violation은 warning-only로 두지 않음 (release-blocking) | UNSUPPORTED_DECISION (외부 source 직접 증명 없음 — 조직 policy 결정) | team-policy |
release-blocking 강도 자체의 외부 표준 부재 |
| D2 | optional adapter test는 adapter enabled matrix에서만 실행 | raw/official-docs/ci-github-actions-vs-gitlab-comparison.md#CIGG-C3 (GitHub Actions needs key 로 job 의존성 명시) |
official-vendor-doc (matrix job 표현은 vendor docs 에서 직접 지원) |
strategy.matrix 의 정확한 표현은 본 인용 범위 밖 — 별도 GitHub Actions matrix 문서 raw 등록 권고 |
| D3 | OpenAPI drift, registry drift, security/privacy leakage, architecture boundary, sample removal smoke 는 release-blocking | raw/official-docs/ci-openapi-snapshot-diff-tooling.md#CIOS-C4, raw/official-docs/ci-openapi-snapshot-diff-tooling.md#CIOS-C5, raw/official-docs/ci-github-actions-vs-gitlab-comparison.md#CIGG-C2 |
official-vendor-doc |
breaking change 판정 규칙 차이 (openapi-diff vs oasdiff) 별도 검증 필요 |
| D4 | warning-only는 dependency freshness advisory 처럼 release artifact correctness 를 직접 깨지 않는 항목에만 허용 | UNSUPPORTED_DECISION (외부 source 직접 증명 없음 — 조직 분류 정책) | team-policy |
freshness advisory 와 vulnerability advisory 의 경계 정의 필요 |
| D5 | vulnerability scanner = Trivy (image + dependency), suppression 은 trivy-ignore + PR review approval 필수 |
UNSUPPORTED_DECISION (Trivy 공식 docs raw source 없음) — + OWNER_AMBIGUITY: scanner tool 선택 은 본 branch(gate wiring) 범위 밖 후보. raw/branch-notes/feature-dependency-vulnerability-management-contract(현재 scanner 미결) 또는 severity 정책 owner raw/branch-notes/feature-build-release-supply-chain-contract 로 위임 권고 (§Audit) | team-convention |
Trivy 공식 페이지 raw source 보강 필요 + tool 선택 owner 미확정 |
| D6 | OpenAPI drift ground truth = code-generated snapshot (springdoc 등), hand-maintained 는 forbidden | raw/official-docs/ci-openapi-snapshot-diff-tooling.md#CIOS-C1, raw/official-docs/ci-openapi-snapshot-diff-tooling.md#CIOS-C2, raw/official-docs/ci-openapi-snapshot-diff-tooling.md#CIOS-C3 |
official-vendor-doc (springdoc runtime introspection 의 공식 동작) |
springdoc 은 dynamic routing (WebFlux functional routes) 일부 누락 위험 — CIOS-C1 의 inferred semantics 한계 |
| D7 | flaky test quarantine bucket 허용, 별도 gradle task 로 분리, sunset deadline 14일 | UNSUPPORTED_DECISION (raw/company-tech-blogs/ci-flaky-test-quarantine-spotify-google.md 는 company-case-study — 공식 best practice 로 단정 불가) |
company-case-study (Spotify/Google/MS 인정 vs Fowler 반대 양립) |
14일 sunset 정량값은 ca-tmpl 절충안 — 외부 표준 부재 |
| D8 | contract test snapshot 의도적 갱신 = breaking change catalog row 인용 + PR label intent:breaking-change-approved escape hatch |
UNSUPPORTED_DECISION (조직 governance 결정 — 외부 source 직접 증명 없음) | team-policy |
label 권한 정책 (feature-contract-verification-test-suite D-Verify Claim 과 cross-link) |
| D9 | 본 branch 가 flaky test quarantine SSOT (sunset 14일), test-taxonomy-fixture-contract 는 consumer | UNSUPPORTED_DECISION (cross-branch ownership 결정) | team-policy |
본 branch ↔ test-taxonomy branch 간 ownership 경계 명문화 |
Note:
ci-flaky-test-quarantine-spotify-google는company-tech-blog카테고리이므로 본 branch 의 quarantine 정책은company-case-study강도만 가지며 official best practice 로 표현 금지.
구현 가이드
결정 (Decisions) 이 "무엇 을 할 것인가" 라면, 본 §는 "어디에 어떻게 구현될 것인가" 의 사전 명세. 본 branch 의 핵심 산출물 카탈로그(gate 목록 + owner 매핑)는
## Gate ↔ Branch Contract Test 소유권 매트릭스(SSOT, 20행). 본 §는 그 매트릭스가 담지 못하는 wiring 메커니즘(needs/if 위상, OpenAPI gate step, flaky 강제, escape hatch, 위임 경계)을 결정·근거 reference 와 함께 명세한다.
1. Gate 위계 — release-blocking vs warning-only 분류 규칙
Trace: D1 (contract violation = release-blocking) + D3 (release-blocking 목록) + D4 (warning-only 한정). Supporting:
CIGG-C2(stages↔needs gate 의존성 모델), team-policy.
- UNSUPPORTED_IMPL_DECISION: "release-blocking" 강도 자체(D1/D4)는 조직 policy — 외부 표준 부재. trade-off: 엄격 차단(merge 속도 ↓, 계약 안전 ↑) vs warning-only 완화(반대). freshness advisory 와 vulnerability advisory 의 경계(D4 Open Risk)도 조직 분류.
- 분류 규칙: release artifact correctness 를 직접 깨는 gate = release-blocking(D3 목록 + 매트릭스 release-blocking 열), freshness advisory 류만 warning-only(D4).
- 전체 gate 목록·owner·release-blocking 여부 =
## Gate ↔ Branch Contract Test 소유권 매트릭스(SSOT). 본 sub-section 은 판정 규칙만, 카탈로그는 매트릭스가 소유(중복 금지).
2. GitHub Actions gate 위상 (needs
Trace: D2 (optional adapter = enabled matrix only) + D3. Supporting:
CIGG-C3(job 의존성 =needskey),CIGG-C2(GitLabstages↔ GitHubneeds매핑).
- UNSUPPORTED_IMPL_DECISION: ①
strategy.matrix의 정확한 yaml shape(adapter-enabled 조합 표현) —CIGG-C3는needskey 만 보장, matrix 표현은 인용 범위 밖. trade-off: 별도 GitHub Actions matrix vendor 문서 raw 등록 필요(D2 Open Risk). ② fan-in 시 status 전파(if: success()vsif: always())의 정확한 규칙 —CIGG-C3미보장(§엣지 Claim 1 로 검증 위임).
- 각 contract-test job 은 release job 의
needs:의존성으로 선언,if: success()로 release gate. - optional adapter test(D2)는
strategy.matrix의 adapter-enabled 조합에서만 실행 → 매트릭스 행 "integration test (optional adapter matrix) | true if matrix enabled".
3. OpenAPI drift gate
Trace: D6 (ground truth = code-generated snapshot, hand-maintained forbidden) + D3 (release-blocking). Supporting:
CIOS-C1/C2/C3(springdoc runtime introspection),CIOS-C4(openapi-diff 3.x 비교),CIOS-C5/C6(oasdiff breaking 서브명령).
- UNSUPPORTED_IMPL_DECISION: ① gradle task 명
openapiCheckSnapshot— note 자체 명명, 인용 외. trade-off: 명명 임의(되묻기 방지용 고정). ② exit-code 기반 차단 —CIOS-C5가 breaking 시 non-zero exit 을 직접 보장하지 않음(§엣지 Claim 2 검증 위임).
- baseline
openapi-snapshot.yaml(checked-in) vs build 시 springdoc-generated OpenAPI 를oasdiff breaking(또는 openapi-diff)으로 비교, breaking 1건+ 이면 release-block. - 알려진 한계: springdoc 은 WebFlux functional route 등 dynamic routing 일부 누락 가능(
CIOS-C1).
4. Flaky test quarantine bucket (본 branch SSOT, 14d sunset)
Trace: D7 (quarantine bucket + 별도 gradle task + 14일 sunset) + D9 (본 branch = SSOT, test-taxonomy = consumer). Supporting:
company-case-study(Spotify/Google/MS) + team-policy.
- UNSUPPORTED_IMPL_DECISION:
@QuarantinedSinceannotation 명 + CI step 의 14일 초과 build-fail 자동 강제 메커니즘 — 외부 source 없음(company-case-study 는 quarantine 인정 만, 14d 정량·강제 메커니즘 무). trade-off: 14d 는 ca-tmpl 절충값; 자동 강제 미구현 시 수동 리뷰로 대체(§엣지 Claim 5).
- quarantine bucket = 별도 gradle task 로 분리(메인 gate 에서 격리). raw/branch-notes/feature-test-taxonomy-fixture-contract 는 flaky 발생 시 본 bucket 을 참조하는 consumer.
5. Snapshot 의도적 갱신 escape hatch
Trace: D8 (breaking change catalog row 인용 + PR label
intent:breaking-change-approved). Supporting: team-policy (governance).
- UNSUPPORTED_IMPL_DECISION: label 부여 권한 정책(누가 label 을 달 수 있나) — 외부 source 없음. trade-off: branch protection + CODEOWNERS 로 label 권한 제한 필요(§엣지 Claim 6); 미설정 시 누구나 우회.
- contract test snapshot 의 의도적 갱신은 breaking change catalog row 를 인용하고 PR 에
intent:breaking-change-approvedlabel 부여로만 통과.
6. 위임된 tool
Trace: D5 (vulnerability scan). 본 branch 는 gate wiring owner — 아래 gate 의 실행/release-blocking 배선 은 in-scope 이나, tool 선택·severity·정책 결정 은 전용 owner branch 로 위임. 매트릭스의 owner 열이 위임 대상을 가리킨다(단 슬러그 drift 는 §Audit
OWNER_BRANCH_DRIFT참조).
- UNSUPPORTED_IMPL_DECISION: D5 의 Trivy tool 선택 은 본 branch 결정 범위 밖 후보 — 전용 vuln branch 미결. trade-off: 본 branch 는 vuln gate 의 release-blocking 배선만 소유, scanner 선택은 위임/확정 필요(§Audit OWNER_AMBIGUITY).
| Gate (wiring in-scope here) | tool/policy 결정 owner (위임) |
|---|---|
| vulnerability scan (Trivy) | tool 선택 = [raw/branch-notes/feature-dependency-vulnerability-management-contract] / severity 정책 = raw/branch-notes/feature-build-release-supply-chain-contract |
| secret scan (gitleaks) | raw/branch-notes/feature-secrets-config-source-contract |
| SBOM / Cosign / SLSA / license | raw/branch-notes/feature-build-release-supply-chain-contract |
| format / lint (tool + ruleset) | raw/branch-notes/feature-static-analysis-quality-contract (매트릭스 "(toolchain)" 셀의 실제 owner — parent §2051) |
| container image scan | raw/branch-notes/feature-container-runtime-contract |
| .env.example drift | raw/branch-notes/feature-env-driven-runtime-configuration |
Gate ↔ Branch Contract Test 소유권 매트릭스
모든 gate는 단일 owner branch contract test를 실행. release-blocking 여부 명시.
| CI gate | release-blocking | owning branch contract test |
|---|---|---|
| format / lint | true | (toolchain) |
| unit test | true | test-taxonomy-fixture-contract |
| architecture test (ArchUnit) | true | architecture-enforcement-rules |
| envelope/error contract test | true | contract-verification-test-suite (envelope) |
| log/MDC contract test | true | contract-verification-test-suite (log) |
| env contract test | true | contract-verification-test-suite (env) |
| registry contract test | true | contract-verification-test-suite (registry) |
| OpenAPI drift | true | contract-verification-test-suite (OpenAPI) |
| schema drift (JSON serialization) | true | contract-verification-test-suite (schema) |
| integration test (default profile) | true | test-taxonomy-fixture-contract |
| integration test (optional adapter matrix) | true if matrix enabled | integration-adapter-templates |
| sample removal smoke | true | sample-removal-adoption-contract |
| SBOM generation | true | build-release-supply-chain |
| signed artifact (Cosign) verification | true | build-release-supply-chain |
| SLSA provenance attestation | true | build-release-supply-chain |
| vulnerability scan (Trivy) high/critical | true | build-release-supply-chain |
| license scan (NOTICE compliance) | true | build-release-supply-chain |
| secret scan (gitleaks) | true | secrets-config-source |
| .env.example drift | true | env-driven-runtime-configuration |
| container image scan (Trivy image) | true | container-runtime-contract |
⚠️ owner 열 슬러그 drift —
build-release-supply-chain→feature-build-release-supply-chain-contract,secrets-config-source→feature-secrets-config-source-contract,(toolchain)(format/lint) →feature-static-analysis-quality-contract. 상세·근거는 §Audit & FindingsOWNER_BRANCH_DRIFT. (사용자 결정 영역이므로 자동 rewrite 하지 않고 정합 권고만 — §구현 가이드 §6 및 §엣지·실패·의존 의존 목록은 정합된 슬러그 사용.)
Gate Matrix (deprecated)
CI Gate 전체 목록과 owner branch 매핑은 위 "Gate ↔ Branch Contract Test 소유권 매트릭스"가 SSOT. 별도 Gate Matrix 양식은 deprecated.
테스트 계약
- contract test result gate: GitHub Actions matrix에서
contract-testjob의 status가failure이면 workflow status도failure여야 함. 측정 방법: workflow yaml의needs: [contract-test]의존성 +if: success()gate 명시 verify. 누락 시 fail. - OpenAPI drift gate:
openapi-diff또는 동등 도구를openapi-snapshot.yaml(checked-in baseline) vs build 시 generated OpenAPI과 비교. diff 결과에 breaking change가 1건이라도 있으면 release-block. 측정 방법: CI step./gradlew openapiCheckSnapshotexit code 0 verify. - high/critical vulnerability 차단 정책이 없으면 실패.
- sample removal smoke gate: workflow yaml에
sample-removal-smokejob이 정의되고 release-blocking matrix에 포함되어 있어야 함. 측정 방법: workflow yaml grep onsample-removal-smoke+ matrix.profile에sample-off포함 verify.
엣지·실패·의존
R4 캡처용. 정상 경로 외에 구현 중 부딪힐 실패/엣지/다른 계약 의존을 미리 열거. 각 실패 경로는 §Claims To Verify 항목과 1:1 대응(검증 위임).
실패·엣지 경로
- needs/if fan-in status 전파: 실패한 matrix job 이 release job 으로 failure 를 전파하는가 —
CIGG-C3가if: always()등 정확한 fan-in 규칙 미보장. 기대: gate 1건 실패 → release block (§Claims C1). - oasdiff exit code semantic: breaking change 발생 시
oasdiff breaking이 non-zero exit 인가 —CIOS-C5미보장. 기대: breaking 1건 → exit != 0 → CI fail (§Claims C2). - Trivy false negative / suppression bypass: CVE DB 갱신 지연, 또는 무단
trivy-ignore추가로 우회. 기대: known CVE → fail, 무단 suppression PR review 없이 차단 (§Claims C3). - flaky 14d sunset 자동 강제 부재:
@QuarantinedSince류 annotation 없으면 14일 초과를 감지할 수 없음. 기대: 14일 초과 → build fail (§Claims C5). - label escape-hatch 무단 사용: label 부여 권한 정책 부재 시 누구나
intent:breaking-change-approved로 우회. 기대: branch protection + CODEOWNERS 로 권한 제한 (§Claims C6). - gate matrix ↔ 실제 contract test 불일치: 20행 표의 owning branch 가 실제 contract test 와 어긋남(슬러그 drift 포함, §Audit). 기대: lint script 로 표 ↔ 코드 cross-check (§Claims C7).
다른 계약 의존 (delegated owner = consume 대상)
본 branch 는 아래 owner branch 의 contract test 를 release-blocking gate 로 consume 한다. 해당 계약이 바뀌면 본 branch 의 gate 실패 조건이 변동된다 (R4 IMPLICIT_DEPENDENCY 명시).
- raw/branch-notes/feature-contract-verification-test-suite — envelope/log/env/registry/OpenAPI/schema contract test 를 gate 로 consume.
- raw/branch-notes/feature-test-taxonomy-fixture-contract — unit/integration test gate + flaky quarantine consumer(D9: 본 branch 가 quarantine SSOT).
- raw/branch-notes/feature-architecture-enforcement-rules — ArchUnit architecture test gate.
- raw/branch-notes/feature-build-release-supply-chain-contract — SBOM/Cosign/SLSA/license/vulnerability-severity gate (severity 정책 owner).
- raw/branch-notes/feature-secrets-config-source-contract — secret scan (gitleaks) gate.
- raw/branch-notes/feature-env-driven-runtime-configuration — .env.example drift gate.
- raw/branch-notes/feature-container-runtime-contract — container image scan gate.
- raw/branch-notes/feature-integration-adapter-templates — optional adapter test matrix.
- raw/branch-notes/feature-sample-removal-adoption-contract — sample removal smoke gate.
- raw/branch-notes/feature-static-analysis-quality-contract — format/lint tool + ruleset (매트릭스 "(toolchain)" 셀의 실제 owner; 본 branch 는 threshold/gate wiring 만).
- raw/branch-notes/feature-dependency-vulnerability-management-contract — vulnerability scanner tool 선택(D5 위임 후보, 현재 미결).
검증해야 할 주장
공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다. 구현 전/중/후에 실제로 검증해야 하는 주장을 분리.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
GitHub Actions needs: + if: success() 조합이 ca-tmpl 11 release-blocking gate 모두를 강제 |
CIGG-C2/C3 는 매핑 가능성만 보장, if: 의 fan-in 시 status 전파 규칙은 인용 범위 밖 |
의도적 fail job 을 matrix 에 추가 → 후속 release job 이 실제로 block 되는지 verify | partially-implemented — release-gate 를 if: always() + needs.*.result 스캔으로 구현(success() 단독은 skipped→차단 실패임을 확인). CI 실제 fail 전파 = needs-confirmation |
openapi-diff 또는 oasdiff 의 exit code 가 breaking change 발생 시 non-zero |
CIOS-C5 는 breaking 검출 기능만 보장, exit code semantic 명시 없음 |
의도적 breaking change PR 생성 → oasdiff breaking exit code != 0 verify |
locally-verified — ca-tmpl 은 oasdiff 대신 openapiCheckSnapshot(Gradle Test, 스냅샷 byte-compare) 채택; 로컬 exit 0 확인. drift 시 fail 은 OpenApiDriftContractTest 가 보장 |
| Trivy high/critical 차단 정책이 false negative 없이 동작 | Trivy CVE DB 갱신 주기 / suppression 우회 가능성 | 의도적 CVE-known dependency (예: log4j 2.14) 추가 → CI fail verify; trivy-ignore 무단 추가가 차단되는지 verify |
delegated — dependency-vulnerability.yml(feature-dependency-vulnerability-management-contract) 소유. 본 branch 는 gate-matrix 에서 release-blocking 으로 배선만 |
| sample-removal-smoke job 이 release-blocking matrix 에 실제 포함됨 | workflow yaml 의 matrix 구성 검증 부재 | workflow yaml grep on sample-removal-smoke + matrix.profile 에 sample-off 포함 verify |
implemented — sample-removal-smoke 잡 + strategy.matrix.profile: [sample-off] 존재, release-gate needs 포함. SampleRemovalSmokeContractTest 로컬 통과 |
| flaky quarantine bucket 의 14일 sunset 이 자동 강제 | sunset deadline 의 자동 감지 메커니즘 부재 가능 | quarantine bucket 의 test 별 @QuarantinedSince annotation + CI step 으로 14일 초과 시 build fail verify |
implemented (locally-verified) — @Tag("quarantine") + repo-루트 flaky-quarantine.yaml + verifyQuarantineSunset(check 연결). over-age 170일 positive control fail 확인. (annotation 대신 레지스트리 채택 — §진행 중 메모) |
intent:breaking-change-approved label escape hatch 가 무단 사용 차단 |
label 추가 권한 정책 부재 시 누구나 우회 | branch protection + CODEOWNERS 로 label 권한 제한 + audit log 점검 | partially-implemented — breaking-change-approval 잡(governed snapshot 변경 시 label 강제) + CODEOWNERS(snapshot/approved 경로). branch protection "Require Code Owners" 활성화는 운영 설정(미적용) = needs-confirmation |
| 20개 gate 표의 owning branch 매핑이 실제 contract test 와 일치 | 표만 있고 cross-check 부재 + 슬러그 drift(§Audit) | 각 owning branch 의 contract test 코드 grep + 본 표와 일치 verify (수동 또는 lint script) | implemented (locally-verified) — .github/ci-gate-matrix.yml(20행) + verify-gate-matrix.sh. 로컬 PASS(16 verified + 4 delegated-pending). 슬러그는 §Audit 정합본 사용 |
관심사 커버리지 (coverage-auditor 자동 생성 — seed)
/coverage가 채우는 생성물. 아래는/branch-spec(2026-06-15)이 governing doc wiki/projects/ca-tmpl/devops-ci-supply-chain-dx + parent §538 "CI Quality Gates" 관심사로 seed 한 것 — coverage-auditor 가 검증/정정한다. 상태:covered-here/delegated/missing.
| 관심사 (governing §538 CI Quality Gates) | 상태 | owner | 심각도 | 근거 |
|---|---|---|---|---|
| format/lint/test/contract/OpenAPI drift/security scan 이 분리된 gate 로 실행 | covered-here | — | — | 매트릭스 20행 + D2/D3 |
| merge 전 실패 가능 gate vs warning-only gate 구분 | covered-here | — | — | D1, D3, D4 / §구현 §1 |
| contract violation = release-blocking (warning-only 불가) | covered-here | — | — | D1 |
| optional adapter test = adapter enabled matrix only | covered-here | — | — | D2 / §구현 §2 |
| OpenAPI drift ground truth = code-generated snapshot | covered-here | — | — | D6 / §구현 §3 |
| flaky test quarantine + sunset 정책 | covered-here | — | — | D7, D9 / §구현 §4 |
| vulnerability scan tool 선택 | delegated | raw/branch-notes/feature-dependency-vulnerability-management-contract (미결) | Should-fix | OWNER_AMBIGUITY: 위임 링크 존재, 단 owner 의 scanner 미결 (§Audit) |
| vulnerability severity → release-block 정책 | delegated | raw/branch-notes/feature-build-release-supply-chain-contract | OK | §구현 §6 |
| secret scan (gitleaks) tool | delegated | raw/branch-notes/feature-secrets-config-source-contract | OK | §구현 §6 / §엣지 의존 |
| SBOM / Cosign / SLSA / license | delegated | raw/branch-notes/feature-build-release-supply-chain-contract | OK | §구현 §6 |
| format/lint tool + ruleset | delegated | raw/branch-notes/feature-static-analysis-quality-contract | OK | parent §2051 / §구현 §6 |
| container image scan | delegated | raw/branch-notes/feature-container-runtime-contract | OK | §구현 §6 |
Audit & Findings (2026-06-15 — /branch-spec ground-truth 대조)
ca-tmpl
docs/registries/*.yaml+src/+ sibling branch-notes 대조 결과. 사용자 작성 결정 영역(매트릭스 owner 열, §완료 후 wiki 추출 대상)은 자동 rewrite 하지 않고 정합 권고만 (CLAUDE.md §15.5 R3,/branch-spec§2 drift 규칙). 신규 작성 섹션(§구현 가이드 §6, §엣지·실패·의존, §Coverage)은 정합된 슬러그 사용.
- OWNER_BRANCH_DRIFT (Gate matrix owner 열):
build-release-supply-chain→ 실제 branch-note 슬러그feature-build-release-supply-chain-contract(존재 확인). 권고: 매트릭스 5개 행(SBOM/Cosign/SLSA/vuln/license) owner 정합.secrets-config-source→ 실제feature-secrets-config-source-contract(docs/registries/secrets-classification.yamlowner_branchSSOT 와 일치). 권고: secret scan 행 정합.(toolchain)(format/lint 행) → 실제 ownerfeature-static-analysis-quality-contract(parent §2051: "tool 선택 + 룰셋" owner; 본 branch 는 threshold/gate wiring 만). 권고: owner 명시.
- OWNER_AMBIGUITY (D5 — vulnerability scanner tool 선택): scanner tool 선택 의 owner 미확정. 전용
feature-dependency-vulnerability-management-contract는 현재 scanner 미결,feature-build-release-supply-chain-contract는 severity 정책만 소유. 권고: 본 branch 는 vuln gate 의 release-blocking 배선만 유지하고, Trivy tool 선택 결정은 dependency-vulnerability 또는 supply-chain owner 로 위임/확정. - EXTRACTION_TARGET_DRIFT (§완료 후 wiki 추출 대상): 지정 경로
wiki/projects/ca-skeleton-operational-contract.md는 wiki 파일로 존재하지 않음(그 슬러그는raw/project-notes/). 실제 CI canonical = wiki/projects/ca-tmpl/devops-ci-supply-chain-dx (§CIdocumented-only, line 41~47). 권고: 추출 대상 정합. - NO_CI_WORKFLOW (ground truth, non-blocking): ca-tmpl 에
.github/workflows/부재 → 본 branch 의 모든 gate 는documented-only/planned단계. 노트 self-report(§Cluster, parent §CI documented-only)와 일치 —actually-implemented과장 없음. drift 아님, 현황 기록.
완료 후 wiki 추출 대상
wiki/projects/ca-skeleton-operational-contract.md의 CI quality gate canonical section. (⚠️ 경로 drift — 실제 canonical 은 wiki/projects/ca-tmpl/devops-ci-supply-chain-dx, §AuditEXTRACTION_TARGET_DRIFT참조.)
마주친 문제
- 2026-06-20: 구현 중 회피한 함정 3종 — (1)
if: success()fan-in aggregator 는 상위 실패 시 skipped(차단 아님) →always()+result 스캔으로 전환; (2)/docs전체 gitignore → CI-read 신규 파일을 tracked 경로로(레지스트리 = repo 루트, gate-matrix =.github/); (3) Gradle 빈 tag 버킷 Test 실패 →failOnNoDiscoveredTests=false. 상세: raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20. - 2026-06-20 (CI 실관측 + 사용자 결정): 사용자가 워크플로를 실제 CI 러너에서 돌려
verifyEnvKeys: missing docs/registries/env-keys.yaml→BUILD FAILED확인. 핵심 트레이드오프 부상 — registry 게이트를 "부재 시 skip" 으로 완화하면 CI green 이지만 CI 에서 vacuous(계약 미강제) → 본 branch 목표("계약을 CI 에서 강제")와 정면 충돌. docs 읽는 contract 테스트 18/21 이 이미 skip-tolerant, verifyEnvKeys 만 throw outlier 임을 확인. 사용자에게 옵션 제시 → Option 1: registries 커밋 채택..gitignore를/docs/*+!/docs/registries/로 좁혀 운영 레지스트리 7개만 추적(나머지/docs는 private 유지).verifyEnvKeys: OK — 99 env keys / 74 required placeholders / 84 APP_ keys재확인. 게이트가 fresh checkout 에서 실제 강제됨 =locally-verified(CI 재실행needs-confirmation). - 2026-06-20 (CI 3차 — quarantine 메커니즘 첫 실사용): full
check에서PrivacySettingsTest.blankSalt_warnsAndFallsBackToDevSentinel(CapturedOutput)1건만 실패(487 tests, 1 failed) → release-gate 가 다시 정확히 차단(quality-gates: failure→::error::release-gate), Claim C1 재실증. 원인(증거):CapturedOutput이 JVM-전역 async logback appender(logback-spring.xml:23ASYNC_ENABLEDdefaultValue=true →:126MetricsAsyncAppenderon root)와 race — sibling@SpringBootTest(ActuatorSecurityHttpTest 등 4종)가 그 async appender 를 설치하면, 경량ApplicationContextRunner테스트의log.warn이 worker 스레드에서output.getOut()읽은 뒤 flush → 단언 실패. 순서/타이밍 의존(로컬 단독·full 모두 통과 = 이기는 순서, CI = 지는 순서). 처리: 사용자 결정 격리(quarantine) — flaky 한blankSalt메서드에만@Tag("quarantine")(realSalt 는 경고 미발생이라 async 무관, 제외) +flaky-quarantine.yaml등록(reason + tracking_issue(TODO, 머지 전 실 Gitea 이슈로 교체) +quarantined_since: 2026-06-20, sunset 2026-07-04). 검증:verifyQuarantineSunset: OK — 1 registered, 1 tagged, drift guard simple-name suffix 매칭(build.gradle:670) 정합,:app-bootstrap:test전체 BUILD SUCCESSFUL(flaky 제외),quarantineTest가 1건 비차단 실행(tests=1 failures=0),check verifyPublicPathSnapshotBUILD SUCCESSFUL. 잠복 위험: 같은 모듈LoggingSettingsTest(badTimezone/badAsyncQueueSizewarnsAndFallsBack)도 동일 CapturedOutput+async race 패턴 — 이번엔 미발생이나 다른 순서에서 재현 가능. 근본수정(sunset 내 owner 몫):logback-test.xml로 test 시 async 비활성, 또는ListAppender직접 단언으로 stdout race 제거 — 한 번에 이 클래스 전체 flake 해소.
묶음
- raw/company-tech-blogs/ci-flaky-test-quarantine-spotify-google
- raw/official-docs/ci-github-actions-vs-gitlab-comparison
- raw/official-docs/ci-openapi-snapshot-diff-tooling
- raw/official-docs/dx-mise-asdf-tool-versioning
- raw/official-docs/dx-testcontainers-java-best-practices
- raw/official-docs/supply-chain-cosign-keyless-sigstore
- raw/official-docs/supply-chain-slsa-provenance-framework
2026-06-20 구현 단계에서 errors / blog-topics / interview-prep 파생 자료 누적.
오류 기록 (본 feature 작업 중 발생)
- raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20 — fan-in skip 함정 + gitignored 설정 CI 의존 + 빈 tag 버킷.
Blog topics (이 작업에서 나온 글감)
- raw/blog-topics/ci-gate-wiring-vs-policy-ownership-2026-06-20 — gate wiring vs policy 소유권 분리 + quarantine sunset 강제.
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- raw/interviews/ci-release-gate-fan-in-blocking-2026-06-20 — fan-in 으로 release 차단을 보장 하는 법.
관련 일일 노트
이 브랜치를 작업한 날짜들. 양방향 nav 유지.
- 2026-06-20 — gate wiring 구현(워크플로 + gate-matrix + 크로스체크 + flaky quarantine sunset + escape-hatch 거버넌스). documented-only → actually-implemented(
locally-verified; CI 실행needs-confirmation). - 2026-06-20 (후속) — CI 실관측으로 verifyEnvKeys 실패 → docs/registries 커밋(gitignore 좁힘, 사용자 Option 1)으로 registry 게이트 CI 강제 회복. PR 템플릿 한국어화.
- 2026-06-20 (CI 속도 최적화 — 사용자 결정 "gradle 잡 통합"): CI wall-clock ~10분+ 원인 = 게이트별 잡 분리로 단일 self-hosted 러너가 잡마다 checkout+setup-java+Gradle캐시+재컴파일 반복(특히
quality-gates의check5m14s 외에 openapi-drift/sample-removal/security-snapshot이 check가 이미 실행하는 테스트를 재실행, optional-adapter-matrix는 테스트 1개에 app-bootstrap 테스트를 4× 재컴파일). 해결: gradle 잡 4개 제거하고./gradlew check verifyPublicPathSnapshot단일 invocation으로 통합(9잡→5잡, gradle 잡 6→2). 게이트 강도 불변(check가 전 테스트 실행, gate-matrix-lint가 매트릭스↔코드 정합 유지). 매트릭스의 optional-adapter 행 mechanism을 workflow-job→contract-test(OptionalAdapterConditionalExecutionContractTest, check 내 실행)로 정합. gate-matrix-lint PASS(20=16+4) 유지. - 2026-06-20 (CI 2차 — 게이트 배선 검증 성공 + 2차 수정): release-gate fan-in 이
quality-gates: failure+breaking-change-approval: failure를 정확히 감지·차단(::error::release-gate: ... failed) → Claim C1 실증 완료. 두 실패 모두 원인 규명·수정: (1) registries 만 커밋해docs/runbooks/부재 → Runbook/BackgroundJobErrorCode 계약 5건이 skip→fail(runbook 파일 dangling).mv docs/runbooks로 로컬 재현 후 gitignore 에!/docs/runbooks/추가(45개 runbook 추적). (2) breaking-change governed 정규식에ci-gate-matrix.yml(config)을 과포함 → 매트릭스 생성 PR 이 라벨 강요당함. governed 를 OpenAPI 스냅샷·*.approved.*로 한정(config 는 CODEOWNERS+lint 로 보호). checkstyleTest ERROR 대량은 비차단 노이즈(static-analysis branch 소유,ignoreFailures=true) — 본 branch 실패 원인 아님. - 2026-06-20 (CI 3차 — quarantine 첫 실사용): full
check에서PrivacySettingsTest.blankSalt(CapturedOutput) 1건 flaky 실패 → release-gate 재차단(Claim C1 재실증). 원인 = async logback(logback-spring.xmlASYNC_ENABLED 기본 true) + sibling@SpringBootTest가 설치한 JVM-전역 appender 와의 stdout race. 사용자 결정 격리:blankSalt메서드만@Tag("quarantine")+flaky-quarantine.yaml등록(14d sunset).verifyQuarantineSunset OK(1/1),quarantineTest1건 비차단 실행,check verifyPublicPathSnapshotBUILD SUCCESSFUL. 잠복:LoggingSettingsTest동일 패턴. 근본수정(owner):logback-test.xmlasync-off 또는 ListAppender 단언. 상세 → raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20 (Trap 4 추가).
완료 후 정리
머지/종료 시점에 채움.
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):