Files
llm-wiki/raw/branch-notes/feature-ci-quality-gates-contract.md

43 KiB
Raw Permalink Blame History

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
ca-skeleton
wiki/projects/ca-tmpl/devops-ci-supply-chain-dx
branch
ca-skeleton
ci
quality-gate
contract-test
2026-05-22 in-progress BR-CA-SKELETON-OPERATIONAL-CONTRACT-028 project-work-item ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-028
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1
1 e13ec9fd666d546ce8cb089f48f43da5ed3d77a72d0c6edc691bad11e65956e8

branch: feature-ci-quality-gates-contract

Layer: raw/branch-notes/ — skeleton 계약이 문서에만 남지 않도록 CI에서 강제할 quality gate 기준을 정의합니다.

부모 (필수)

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):
  • 검토한 대안:
    • 대안 1: GitLab CI vs GitHub Actions — 동일 source에서 비교. 선택 조건: ca-tmpl repo 가 GitHub 호스팅 → GitHub Actions 채택; GitLab 호스팅으로 이전 시 needsstages 매핑(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은 절충안
  • 비교 핵심: 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_docs frontmatter 추가, §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(test excludeTags 'quarantine' + quarantineTest 버킷 + verifyQuarantineSunset 14일 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 의 @QuarantinedSince custom 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 소유).
    • 검증(로컬): verifyQuarantineSunset 3종 control(empty→OK / over-age 170d→fail / drift unregistered→fail), :shared-contract:quarantineTest BUILD 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.

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-googlecompany-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 의존성 = needs key), CIGG-C2 (GitLab stages ↔ GitHub needs 매핑).

  • UNSUPPORTED_IMPL_DECISION: ① strategy.matrix 의 정확한 yaml shape(adapter-enabled 조합 표현) — CIGG-C3needs key 만 보장, matrix 표현은 인용 범위 밖. trade-off: 별도 GitHub Actions matrix vendor 문서 raw 등록 필요(D2 Open Risk). ② fan-in 시 status 전파(if: success() vs if: 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: @QuarantinedSince annotation 명 + CI step 의 14일 초과 build-fail 자동 강제 메커니즘 — 외부 source 없음(company-case-study 는 quarantine 인정 만, 14d 정량·강제 메커니즘 무). trade-off: 14d 는 ca-tmpl 절충값; 자동 강제 미구현 시 수동 리뷰로 대체(§엣지 Claim 5).

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-approved label 부여로만 통과.

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-chainfeature-build-release-supply-chain-contract, secrets-config-sourcefeature-secrets-config-source-contract, (toolchain)(format/lint) → feature-static-analysis-quality-contract. 상세·근거는 §Audit & Findings OWNER_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-test job의 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 openapiCheckSnapshot exit code 0 verify.
  • high/critical vulnerability 차단 정책이 없으면 실패.
  • sample removal smoke gate: workflow yaml에 sample-removal-smoke job이 정의되고 release-blocking matrix에 포함되어 있어야 함. 측정 방법: workflow yaml grep on sample-removal-smoke + matrix.profile에 sample-off 포함 verify.

엣지·실패·의존

R4 캡처용. 정상 경로 외에 구현 중 부딪힐 실패/엣지/다른 계약 의존을 미리 열거. 각 실패 경로는 §Claims To Verify 항목과 1:1 대응(검증 위임).

실패·엣지 경로

  • needs/if fan-in status 전파: 실패한 matrix job 이 release job 으로 failure 를 전파하는가 — CIGG-C3if: 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 명시).

검증해야 할 주장

공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다. 구현 전/중/후에 실제로 검증해야 하는 주장을 분리.

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 delegateddependency-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.profilesample-off 포함 verify implementedsample-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-implementedbreaking-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.yaml owner_branch SSOT 와 일치). 권고: secret scan 행 정합.
    • (toolchain) (format/lint 행) → 실제 owner feature-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 (§CI documented-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 추출 대상

마주친 문제

  • 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.yamlBUILD 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:23 ASYNC_ENABLED defaultValue=true:126 MetricsAsyncAppender on 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 verifyPublicPathSnapshot BUILD SUCCESSFUL. 잠복 위험: 같은 모듈 LoggingSettingsTest(badTimezone/badAsyncQueueSize warnsAndFallsBack)도 동일 CapturedOutput+async race 패턴 — 이번엔 미발생이나 다른 순서에서 재현 가능. 근본수정(sunset 내 owner 몫): logback-test.xml 로 test 시 async 비활성, 또는 ListAppender 직접 단언으로 stdout race 제거 — 한 번에 이 클래스 전체 flake 해소.

묶음

2026-06-20 구현 단계에서 errors / blog-topics / interview-prep 파생 자료 누적.

오류 기록 (본 feature 작업 중 발생)

Blog topics (이 작업에서 나온 글감)

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

관련 일일 노트

이 브랜치를 작업한 날짜들. 양방향 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-gatescheck 5m14s 외에 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.xml ASYNC_ENABLED 기본 true) + sibling @SpringBootTest 가 설치한 JVM-전역 appender 와의 stdout race. 사용자 결정 격리: blankSalt 메서드만 @Tag("quarantine") + flaky-quarantine.yaml 등록(14d sunset). verifyQuarantineSunset OK(1/1), quarantineTest 1건 비차단 실행, check verifyPublicPathSnapshot BUILD SUCCESSFUL. 잠복: LoggingSettingsTest 동일 패턴. 근본수정(owner): logback-test.xml async-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):