--- title: blog-topic / ci-gate-wiring-vs-policy-ownership source_type: blog-topic status: raw related_branches: [feature-ci-quality-gates-contract] related_projects: [ca-tmpl] tags: [blog-topic, ca-tmpl, ci, github-actions, gradle] created: 2026-06-20 status_label: ready-for-canonical target_audience: backend-engineer inspiration_url: archive_url: --- # blog-topic: ci-gate-wiring-vs-policy-ownership > Layer: `raw/blog-topics/` — 작업·트러블슈팅에서 나온 블로그 글감 원석. > `status_label`: `captured` ## Parent / 부모 - [[raw/branch-notes/feature-ci-quality-gates-contract]] — "어떤 게이트가 도느냐(wiring)" 와 "그 게이트의 도구·임계값(policy)" 을 다른 branch 가 소유하도록 분리한 경험에서 도출. ## 트리거 / Trigger - 트리거 유형: `branch-work` - 트리거 날짜: 2026-06-20 - 트리거 연결 노트: [[raw/branch-notes/feature-ci-quality-gates-contract]] ## 글감 / Topic seed - 한 줄 요약: CI 품질 게이트를 "배선(wiring)" 과 "정책(policy)" 으로 쪼개면, 20개 게이트를 한 워크플로에 욱여넣지 않고도 소유권이 깨끗해지고 게이트가 silent-pass 하지 않는다. - 풀어야 할 질문들: - **소유권 분리.** vulnerability 스캔의 *release-blocking 여부*(wiring)와 *scanner 선택·severity 임계값*(policy)을 왜 다른 owner 가 갖나? → 한 branch 가 둘 다 가지면 정책 변경이 매번 게이트 그래프를 건드려 결합도가 폭증. ca-tmpl 은 `.github/ci-gate-matrix.yml`(20행 SSOT) + `verify-gate-matrix.sh` cross-check 로 "표 ↔ 실제 task/test/job" 정합을 매 PR 강제. - **fan-in 이 차단을 보장하려면.** `needs + if: success()` aggregator 는 상위 실패 시 *skipped* — 차단 안 됨. `always()` + `needs.*.result` 스캔이라야 "1건 실패 → 릴리스 block". (evidence-first: 공식 문서가 보장하는 건 매핑 가능성뿐, 실제 차단은 의도적 실패 잡으로 검증해야.) - **위임 게이트의 정직한 표현.** owner branch 가 아직 없는 게이트(SBOM/Cosign/SLSA/gitleaks)는 가짜 통과 잡으로 채우지 말고 `delegated-pending` 으로 *명시적으로 미구현* 이라 표시 → cross-check 가 개수까지 보고. - 독자가 얻어갈 것: "게이트를 늘리는 것" 보다 "게이트가 실제로 막는지 + 누가 그 정책을 소유하는지" 가 본질이라는 관점. ## 확장 메모 / Expansion notes - 곁가지: flaky quarantine 의 14일 sunset 을 Gradle 거버넌스 태스크(`verifyQuarantineSunset`)로 강제 — `@Tag("quarantine")` 격리 + repo-루트 레지스트리 + drift/sunset 이중 검사. quarantine 이 *영구 주차장* 이 되는 걸 빌드가 막는다. (별도 글감 가능.) - 대비 사례: `.trivyignore` suppression 거버넌스([[raw/blog-topics/trivy-suppression-governance-static-gate-2026-06-20]])와 같은 패턴 — "정책 파일 + CI 필드검증 + CODEOWNERS merge 승인" 삼중 통제. ## 핵심 주장 후보 / Claim candidates - 사실 후보: - CI gate matrix는 gate wiring과 policy owner를 분리해 기록한다. - GitHub Actions fan-in은 `always()`와 `needs.*.result` 확인을 써야 upstream failure가 skipped로 묻히지 않는다. - 의견/해석 후보: - CI gate의 본질은 gate 수가 아니라 실제 차단 여부와 policy ownership의 분리다. ## Outline seed 1. gate wiring과 policy ownership을 분리하는 이유를 설명한다. 2. `needs + if: success()` fan-in의 skipped 함정을 다룬다. 3. delegated-pending gate를 가짜 green으로 만들지 않는 표현 방식을 정리한다. ## Canonical 전환 후보 / Canonical extraction candidates - `wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md` 후보: - CI gate wiring vs policy ownership 글감. - 필요한 추가 검증: - 실제 workflow fan-in 실패 검증과 matrix cross-check 구현 여부. ## Sources / 근거 후보 - [[raw/branch-notes/feature-ci-quality-gates-contract]] - [[raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20]] - [[raw/interviews/ci-release-gate-fan-in-blocking-2026-06-20]] - [[raw/blog-topics/trivy-suppression-governance-static-gate-2026-06-20]] ## 미해결 / Unknown - 아직 확인해야 할 사실: hosted CI에서 fan-in 차단 검증이 수행됐는지. - 과장하면 안 되는 부분: delegated-pending gate를 구현 완료 gate처럼 쓰지 않는다. ## Related / 관련 - 관련 branch: [[raw/branch-notes/feature-ci-quality-gates-contract]] - 관련 error: [[raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20]] - 관련 interview: [[raw/interviews/ci-release-gate-fan-in-blocking-2026-06-20]] - 유사 거버넌스 글감: [[raw/blog-topics/trivy-suppression-governance-static-gate-2026-06-20]] ## Decision / 처리 결정 - 액션: `promote-to-canonical` - 이유: `wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md` 에 CI gate wiring과 policy ownership 분리 글감으로 반영했다. - 다음 단계: target canonical이 아직 `draft` 이므로 `blogify` 전 GitHub Actions fan-in 차단 검증과 delegated-pending 범위를 분리한다.