Files
llm-wiki/raw/branch-notes/feature-operational-runbook-contract.md
T

39 KiB


branch: feature-operational-runbook-contract

Layer: raw/branch-notes/ — 장애 알림 이후 운영자가 확인하고 판단할 기준을 runbook 계약으로 정의합니다.

부모 (필수)

ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다. governing 문서는 wiki/projects/ca-tmpl/observability-log-metric-trace-runbook (§Runbook 슬라이스).

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: 각 failure category에 trigger·diagnosis·recovery drill이 연결된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1 신규 환경의 default 진입 명령은 ./gradlew bootstrap이다 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

목표

alert는 시작점일 뿐입니다. 운영자가 어떤 로그 필드, metric, trace, dependency 상태를 먼저 봐야 하는지 없으면 장애 대응 품질이 사람마다 달라집니다.

  • 이슈:
  • PR:

범위

포함 범위

  • alert별 first check 기준.
  • DB unavailable, dependency timeout, auth failure spike, 5xx spike, queue lag, cache unavailable runbook 기준.
  • degrade/fail-fast 판단 기준.
  • dashboard/log query/runbook link 필드 기준.

제외 범위

  • 실제 on-call 조직 운영.
  • provider dashboard 생성.
  • SLA/SLO 법적 약정.
  • alert severity(P1/P2/P3) 정의 · threshold · dedup/flapping/maintenance-window muteraw/branch-notes/feature-metrics-alerting-contract 가 owner (governing doc §Metric 위임). 본 branch 는 runbook 계약(scheme/link-check/coverage)만. 노트 내 잔존 D5~D8 은 ## Audit & FindingsOUT_OF_BRANCH_SCOPE 참조(사용자 결정 영역이라 본문 보존).

근거 (필수, 최소 1개+)

본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.

Source 정당화하는 결정
raw/official-docs/runbook-pagerduty-incident-response-doc.md PagerDuty 권장 runbook 5단 구조(Purpose / Severity / First check / Mitigation / Escalation
raw/company-tech-blogs/runbook-atlassian-gitops-runbook-as-code.md git-hosted markdown runbook이 Confluence wiki 대비 drift 적고 PR review 가능
raw/company-tech-blogs/runbook-woowahan-incident-techblog.md 국내 사례
raw/official-docs/google-sre-workbook-on-call-monitoring.md D2 (runbook = 운영 계약): SRE-WB-OC-C4/C5/C6 — playbook 구성 요소 + alert↔playbook 1:1 coupling 권고. D10 (Error Registry ↔ Runbook CI gate): SRE-WB-OC-C5/C6 — alert↔playbook coupling 까지만 보증, error-registry 확장은 UNSUPPORTED_EXTENSION. Strength = official-reference (community consensus), NOT official-vendor-doc
raw/official-docs/lychee-link-checker.md D4 (link-check smoke validation): LYCHEE-C1/C2/C3 — Rust async stream-based link checker + Markdown/HTML 1차 지원 + plain text fallback. Strength = official-vendor-doc (project README self-description), 도구 capability 근거로만 사용 (best-practice 주장 금지)
raw/official-docs/prometheus-alertmanager-silences.md D6 (maintenance window P2/P3 mute, P1 유지): ALERTMANAGER-SIL-C1/C2/C3 — silence = 시간 제한 mute + matcher AND 매칭 메커니즘. Strength = official-vendor-doc. severity 기반 P2/P3 vs P1 매핑 정책은 ca-tmpl 자체 결정 (Alertmanager 가 보증하지 않음)

외부 근거 / 대안 조사 (2026-05-22 — Group G-A: Operational runbook)

채택 결정 + 뒷받침

검토 대안 + source

비교 핵심 1줄

runbook:// scheme + git markdown은 service repo PR cycle + link-check + CI coverage gate로 drift 방지가 강점, Confluence wiki는 검색 UX 강점이나 drift, auto-remediation은 자동화 효율 vs mutation risk trade-off.

TODO

TODO drained — 결정은 error.category enum 표 / MDC Key Standard 표 / error.details JSON shape 참조

진행 중 메모

  • 2026-06-14 (/branch-spec): ca-tmpl ground truth 재검증 후 §구현 가이드·§엣지·실패·의존·§Audit & Findings 추가, frontmatter parent_branch/governing_docs 보강, 섹션을 템플릿 순서로 재배치. 기존 D1~D10·표·외부 근거 본문은 verbatim 보존. 핵심 신규 근거 = runbook resolver(runbook://{area}/{scenario}docs/runbooks/{area}-{scenario}.md) 6건 정합 / 34 orphan / 4 unref (grep 2026-06-14) + Category enum 10-value(Category.java) consume 확인 + error-codes.yaml L24-28 의 retryable=true⇒runbook 필수 절(노트 누락 = COVERAGE_DRIFT).

결정 사항

  • 2026-05-22: alert에는 operation, dependency, error.category, error.code, retryable, runbook link가 연결되어야 함.
  • 2026-05-22: runbook은 implementation detail이 아니라 운영 계약의 일부로 관리.
  • 2026-05-22: runbook link 형식은 runbook://{area}/{scenario} 또는 repository relative markdown path만 허용. placeholder/empty link는 canonical promotion 실패.
  • 2026-05-22: runbook link 검증은 link-check smoke로 수행하며 자동화가 없으면 수동 evidence table이 필수.
  • 2026-05-22: alert deduplication window = 5분 (동일 alert key 재발 시 silent). flapping suppression = 15분 내 3회 toggle 시 mute 30분.
  • 2026-05-22: maintenance window 등록 시 P2/P3 알림은 mute, P1은 유지.
  • 2026-05-22: P3 정의 = business hours 대응, on-call page 안 함, dashboard만 갱신.
  • 2026-05-22: P1 발화 임계(2분) + alert dedup window(5분) = 같은 incident의 2nd alert이 5분 내 silent. 5분 후 재발 시 P1 재발화. 의도된 noise 억제 (operator burnout 방지).
  • 2026-05-22: 본 branch는 runbook link 형식과 검증 SSOT. 실 runbook 본문은 ca-tmpl/docs/runbooks/*.md 또는 ca-tmpl repo docs/runbooks/ 에 작성 (Phase D2). 본 branch는 스키마/coverage 정책만.

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 승급 위치

결정-근거 매핑

각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. PagerDuty / Atlassian / 우아한형제들 인용은 모두 company-case-study — 공식 best practice 로 단정 금지. 본 branch 의 다수 결정은 raw 인용 부재로 UNSUPPORTED_DECISION.

Decision ID Decision Supporting Claims Evidence Strength Open Risk
D1 alert 에 operation / dependency / error.category / error.code / retryable / runbook link 6 field 필수 raw/official-docs/runbook-pagerduty-incident-response-doc.md#PD-RB-C2 (alert body 의무 항목), raw/official-docs/runbook-pagerduty-incident-response-doc.md#PD-RB-C3 (runbook link 의무) company-case-study PagerDuty 권장이며 공식 표준 아님. 6 field 의 정확한 enumeration 은 ca-tmpl 자체 결정 — verbatim 인용 부재
D2 runbook 은 implementation detail 이 아닌 운영 계약의 일부로 관리 raw/official-docs/google-sre-workbook-on-call-monitoring.md#SRE-WB-OC-C4 (playbook = severity/impact/debugging/mitigation 을 포함하는 alert 대응 표준 자산), #SRE-WB-OC-C5 (alert 생성 시 대응 playbook entry 함께 생성, stress/MTTR/human-error 감소), #SRE-WB-OC-C6 (각 alert 는 대응 playbook entry 를 가져야 하며 새 alert 는 new code 처럼 review) official-reference SRE Workbook 은 official-reference (community consensus) 이지 official-vendor-doc 이 아님 — playbook 의 markdown 파일 schema 까지는 보증하지 않음 (Usage Boundary). Atlassian raw ATL-RB-C5 는 여전히 needs-confirmation 상태로 보조 근거 보강 권고
D3 runbook link 형식 = runbook://{area}/{scenario} 또는 repository relative markdown path 만 허용, placeholder/empty 는 promotion fail raw/official-docs/runbook-pagerduty-incident-response-doc.md#PD-RB-C4 (예시 runbook 링크 = URL 기반) company-case-study PagerDuty 예시는 https URL 만 표시 — custom scheme runbook:// 은 ca-tmpl 자체 추론, vendor 인용 부재. 해석 규칙은 §구현 가이드 §1 (ground-truth 6건 정합/34 orphan)
D4 runbook link 검증 = link-check smoke + 자동화 없으면 수동 evidence table 필수 raw/official-docs/lychee-link-checker.md#LYCHEE-C1 (fast / async / stream-based Rust link checker), #LYCHEE-C2 (Markdown / HTML / 기타 포맷에서 broken hyperlink + mail address 검출), #LYCHEE-C3 (HTML/Markdown 1차 지원 + 그 외 plain text fallback) official-vendor-doc lychee README 는 self-description — "공식 best practice 도구" 가 아닌 capability 근거로만 사용. wikilink (이중 대괄호(double-bracket)) native 지원 여부는 본 인용 밖 (별도 PoC 필요). 수동 evidence table 의 형식은 lychee 가 보증 안 함. planned — ca-tmpl 에 link-check 스크립트 부재 (grep 2026-06-14)
D5 alert deduplication window = 5분 (동일 key 재발 silent), flapping suppression = 15분 내 3회 toggle 시 mute 30분 UNSUPPORTED_DECISION — Prometheus Alertmanager 또는 PagerDuty deduplication 공식 doc 인용 부재 (정량값). OUT_OF_BRANCH_SCOPE — alerting-routing 영역(§Audit & Findings) unsupported Alertmanager / PagerDuty 공식 doc 인용 권고. 단 이 결정은 runbook 계약이 아닌 alert-routing 계약 → owner 후보 = feature-metrics-alerting-contract 또는 신규 alert-routing branch (Audit 참조). 본 branch 에서 자동조사 보류
D6 maintenance window 시 P2/P3 mute, P1 유지 raw/official-docs/prometheus-alertmanager-silences.md#ALERTMANAGER-SIL-C1 (silence = 주어진 시간 동안 알람 mute = 시간 제한 suppression), #ALERTMANAGER-SIL-C2 (silence 는 routing tree 와 동일하게 matcher 기반 설정), #ALERTMANAGER-SIL-C3 (incoming alert 가 모든 (all) equality/regex matcher 만족 시 silence 적용) official-vendor-doc Alertmanager 는 silence 메커니즘 자체 (시간 제한 mute + matcher AND) 만 보증 — "P2/P3 mute / P1 유지" 라는 severity 기반 정책 매핑은 ca-tmpl 자체 결정 (Alertmanager 가 severity 라는 label 을 표준으로 정의하지 않음). silence 의 start/end grammar / 무한 silence 가능 여부는 본 인용 밖 (UI / API spec 별도). OUT_OF_BRANCH_SCOPE — severity 매핑은 feature-metrics-alerting-contract owner (Audit 참조)
D7 P3 정의 = business hours 대응, on-call page 안 함, dashboard 만 갱신 raw/official-docs/runbook-pagerduty-incident-response-doc.md#PD-RB-C5 (PagerDuty High/Medium/Low/Notification 4단계) company-case-study PagerDuty 4단계와 ca-tmpl P1/P2/P3 의 1:1 매핑은 ca-tmpl 자체 결정 (raw Usage Boundary 명시: "1:1 매핑 보장 안 됨"). RESTATED_FOREIGN_DECISION — P1/P2/P3 severity 정의의 owner 는 feature-metrics-alerting-contract (§P1/P2/P3 정량 기준). reference-only 위임 권고 (Audit 참조)
D8 P1 발화 임계(2분) + alert dedup window(5분) = 2nd alert 이 5분 내 silent, 5분 후 재발화 (operator burnout 방지) UNSUPPORTED_DECISION — 정량값 (2분/5분) 외부 reference 부재 unsupported PagerDuty 또는 Google SRE 의 fatigue prevention doc 인용 권고. OUT_OF_BRANCH_SCOPE — P1 발화 임계(2분)는 feature-metrics-alerting-contractrequired dep unavailable >2분 와 중복(RESTATED_FOREIGN_DECISION); dedup window 부분은 alert-routing. 본 branch 에서 자동조사 보류 (Audit 참조)
D9 본 branch = runbook link 형식 + 검증 SSOT (실 runbook 본문은 Phase D2 ca-tmpl/docs/runbooks/*.md 작성) UNSUPPORTED_DECISION — 내부 스코프 결정 internal-only scope drift 위험 — D2/Phase D2 timing 추적 필요. update 2026-06-14: docs/runbooks/ 에 10개 파일 실재(grep) — 본문 일부는 이미 작성됨. 단 per-code scheme link 40 vs 파일 10 (34 orphan) 으로 coverage 미완 (Audit RUNBOOK_LINK_RESOLUTION_DRIFT)
D10 Error Registry ↔ Runbook coverage CI gate — retryable=false + category ∈ {TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY, AUTH, AUTHZ, RATE_LIMIT, INTERNAL} 인 row 는 runbook link 필수, 누락 시 release-block raw/official-docs/google-sre-workbook-on-call-monitoring.md#SRE-WB-OC-C5 (alert 생성 시 대응 playbook entry 함께 생성이 SRE 일반 관행), #SRE-WB-OC-C6 (각 alert 는 대응 playbook entry 를 가져야 하며 review 대상) — UNSUPPORTED_EXTENSION: 본 raw Usage Boundary 명시 — "error code ↔ runbook 1:1 mapping 이 alert ↔ playbook 1:1 mapping 과 동치라는 점은 SRE Workbook 이 직접 보증하지 않음. error registry 개념 자체가 SRE Workbook 에 등장하지 않음" official-reference (alert↔runbook 까지만) + unsupported-extension (error-registry↔runbook 까지의 확장) SRE Workbook 은 alert↔playbook coupling 만 보증, 이를 error-registry↔runbook coupling 으로 확장 적용 하는 것은 ca-tmpl 자체 결정. CI gate 의 release-block 정책 (자동화 도구 / fail criteria) 외부 reference 부재. COVERAGE_DRIFT 2026-06-14: error-codes.yaml L24-28 은 추가로 "retryable=true 인 모든 row ⇒ runbook 필수" 절을 포함하나 본 row 는 이를 누락 — 정합 권고(Audit 참조). 우아한형제들 raw URL 교체 후 verbatim 재인용 권고

구현 가이드

결정 (Decisions) 이 "무엇 을 할 것인가" 라면, 본 §는 "어디에 어떻게 구현될 것인가" 의 사전 명세. 본 branch in-scope = runbook 계약 (D1 payload runbook field · D2 · D3 scheme · D4 link-check · D9 본문 위치 · D10 coverage gate). alerting-platform 결정(D5~D8)은 §Audit OUT_OF_BRANCH_SCOPE 로 분리 — 본 §에 구현 detail 을 남기지 않음(R3).

계약 값 SSOT: ca-tmpl/docs/registries/error-codes.yaml (owner_branch 다수) + runbook_link 컬럼. Category enum SSOT = feature-operational-error-observability-foundationsrc/shared-contract/.../error/Category.java (10-value, 본 branch 는 consume only).

Trace: D3 / PD-RB-C4. Ground-truth grep 2026-06-14 (ca-tmpl/docs/runbooks/ + error-codes.yaml).

  • UNSUPPORTED_IMPL_DECISION: 첫 /- 로 치환(area 1-segment·scenario 1-segment 가정)은 ca-tmpl 자체 결정 — PagerDuty 인용은 https URL 만 보증. trade-off: scenario 에 / 포함 시 모호 → scenario 는 단일 kebab segment 강제.
입력 변환 결과 상태
runbook://{area}/{scenario} area·scenario 사이 /- (area/scenario 각 단일 segment) docs/runbooks/{area}-{scenario}.md (repo-relative) actually-implemented (6건 resolve 정합: runbook://job/executor-rejecteddocs/runbooks/job-executor-rejected.md 등)
repository relative markdown path 그대로 docs/runbooks/*.md actually-implemented (파일 10건 실재)
runbook://area/scenario (placeholder) / empty promotion fail planned (게이트 미구현)

2. runbook coverage gate (error-registry ↔ runbook)

Trace: D10 / SRE-WB-OC-C5/C6 (alert↔playbook 까지만; error-registry 확장은 UNSUPPORTED_EXTENSION). 정책 값 SSOT = error-codes.yaml L24-28.

  • UNSUPPORTED_IMPL_DECISION: error-registry↔runbook 1:1 강제 + release-block 자동화 도구 선택은 외부 reference 부재. trade-off: alert↔playbook(보증됨)을 error-code 단위로 확장 — 운영상 합리적이나 SRE 문헌이 직접 보증하지 않음.

error-codes.yaml row 판정 (category enum = Category.java 10-value consume):

조건 runbook_link 근거
retryable=false + category ∈ {AUTH, AUTHZ, RATE_LIMIT, INTERNAL, TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY} 필수 D10 / error-codes.yaml L25-26
retryable=true 인 모든 row 필수 error-codes.yaml L28 — 노트 D10 이 누락한 절(Audit COVERAGE_DRIFT)
retryable=false + category ∈ {VALIDATION, NOT_FOUND, CONFLICT, DATA_INTEGRITY} (client-error) null 허용 error-codes.yaml L27
위 필수 조건인데 runbook_link 누락/placeholder release-block D10

Ground-truth coverage 방향 (grep 2026-06-14): 실제 coverage 의 authoritative 방향은 runbook→codesdocs/runbooks/*.md 10개 전부 frontmatter 에 error_codes: [...] 선언(예: dependency-unavailable.md → 7 codes [DEPENDENCY_TIMEOUT, DEPENDENCY_CONNECT_FAILED, DEPENDENCY_DNS_FAILED, DEPENDENCY_CIRCUIT_OPEN, DEPENDENCY_5XX_SERVER, CACHE_UNAVAILABLE, DB_UNAVAILABLE]). 따라서 coverage gate 는 "각 필수 error code 가 정확히 한 runbook 의 error_codes: 리스트에 등장" 으로 구현하는 것이 정합 — error-codes.yaml 의 per-code runbook:// link 를 forward resolve(§1)하는 방식이 아님. 이 reverse-index 가 consolidated(many-codes→one-runbook)를 자연히 허용해 §엣지의 granularity 문제를 설계상 해소.

Trace: D4 / LYCHEE-C1/C2/C3. 전체 planned — ca-tmpl 에 link-check 스크립트/테스트 부재(grep 2026-06-14).

  • UNSUPPORTED_IMPL_DECISION: lychee 가 custom runbook:// scheme + 이중대괄호 wikilink 를 native 지원하는지는 인용 밖 → resolver(§1)가 scheme 을 file path 로 먼저 치환한 뒤 lychee 에 file 모드로 넘기는 2단계 필요. trade-off: 치환 단계 버그 가능 → resolver 단위 테스트로 고정.

판정 항목 (resolve 된 target 파일에 대해): (a) 파일 존재, (b) 본문에 (?i)(TODO|TBD|PLACEHOLDER|FIXME) 미포함, (c) docs/runbooks/template.md 로 시작하지 않음(template 자체는 link target 아님). 1건이라도 위반 시 fail (§테스트 계약과 동치).

Stub gap (grep 2026-06-14): 현재 docs/runbooks/*.md 10개 전부 status: stub frontmatter — placeholder regex 에 안 걸려 stub 이 smoke 통과. → smoke fail 집합에 status: stub(또는 본문 'Stub')을 포함해야 미완 runbook 을 block. docs/runbooks/template.md 는 실재하지 않음 → 규칙 (c)는 현재 no-op(무해, 파일 생성 시 활성).

4. 현재 coverage 상태 (ground-truth 2026-06-15 D1 구현 후)

Trace: D4·D10 의 검증 대상 실측. 이 표가 §Claims To Verify + §Audit RUNBOOK_LINK_RESOLUTION_DRIFT 의 근거.

지표 값 (2026-06-15)
error-codes.yaml 의 distinct runbook:// link 39 (RATE_LIMIT_EXCEEDED 포함)
docs/runbooks/*.md 실파일 45 (기존 10 + 신규 34 + template.md)
{area}-{scenario}.md 규칙으로 resolve OK 39 / 39 (orphan 0)
mandatory code 가 어떤 runbook error_codes: 에도 없음 0 (coverage 100%)
status: stub 본문 (미완) 44 / 44 (전부 stub — Phase D2 예정)
authoritative coverage 방향 runbook error_codes: frontmatter (양방향 SSOT 정합)
STUB_ALLOWLIST 등재 44개 (기존 10 + 신규 34)

이전 값 (2026-06-14): error-codes.yaml 40 link / 실파일 10 / orphan 34 / coverage 미달 다수

주의: 위 "34 orphan" 은 forward (per-code scheme→file) 가정의 수치. 실제 coverage SSOT 는 runbook error_codes: frontmatter(§2 ground-truth note) — 이 방향으로 보면 consolidated 파일이 codes 를 묶어 선언하므로 granularity 는 설계상 해소. 남는 작업: (a) error-codes.yaml 의 모든 필수 code 가 어떤 runbook error_codes: 에도 없으면 = 진짜 missing, (b) 10개 stub 본문 작성(Phase D2), (c) D4/D10 게이트 코드.

field default
link format runbook://area/scenario or docs/runbooks/*.md
required metadata severity, first metric, first log query, dependency owner, rollback/degrade decision
forbidden empty link, TBD, inaccessible URL
verification link-check smoke or manual evidence table

Error Registry ↔ Runbook Coverage

  • CI gate: error registry의 모든 retryable=false + category ∈ {TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY, AUTH, AUTHZ, RATE_LIMIT, INTERNAL} row는 runbook link 필수.
  • 누락 시 release-block. 자동 비교는 verification suite가 수행.

엣지·실패·의존

R4(깊이 게이트) 캡처용. 정상 경로 외에 구현 중 부딪힐 실패/엣지/다른 계약 의존을 미리 열거.

  • 실패·엣지 경로:
    • Orphan runbook link (34건)error-codes.yaml 의 per-code scheme link 40건 중 34건이 대응 파일 없음(§구현 가이드 §4, grep 2026-06-14). link-check(D4) 도입 시 전부 fail. 기대 동작: D10 게이트가 release-block.
    • Granularity (consolidated runbook) — 실파일 4건(auth-token-rotation-failure 등)은 incident-class 단위 consolidated 라 forward per-code scheme link 와 1:1 안 맞음. 단 ground-truth 상 coverage SSOT 는 runbook error_codes: frontmatter(many-codes→one-runbook, §구현 가이드 §2) → 이 방향이면 정상. 남은 결정: forward runbook:// scheme 을 유지(유지 시 alias/redirect 필요) vs error_codes: frontmatter 단일 SSOT 로 수렴 — D11 후보(사용자 영역, Audit RUNBOOK_LINK_RESOLUTION_DRIFT).
    • Custom scheme 비렌더링runbook:// 는 PagerDuty/Slack 등 외부 채널에서 클릭 불가 가능(§Claims To Verify). 기대 동작: alert renderer 가 scheme→repo/https URL 치환 후 발송(planned).
    • Placeholder body — link target 파일이 존재해도 본문에 TODO/TBD/PLACEHOLDER/FIXME 있으면 fail(§테스트 계약 §placeholder).
  • 다른 계약 의존:
    • raw/branch-notes/feature-operational-error-observability-foundationCategory.java (10-value enum) — 본 branch coverage gate(§구현 가이드 §2)가 category 집합을 consume. enum 변경 시 게이트 카테고리 집합 재검토.
    • raw/branch-notes/feature-metrics-alerting-contract 의 alert payload(D10) + P1/P2/P3 severity(§P1/P2/P3 정량 기준) — 본 branch 의 runbook_link field 는 그 alert payload 계약 위에 얹힘. severity/dedup/threshold 의 owner 는 그 branch (본 노트 D5~D8 의 Audit OUT_OF_BRANCH_SCOPE 참조).
    • raw/branch-notes/feature-runtime-health-lifecycle-contract 의 Required vs Optional Dependency Matrix — §테스트 계약의 degrade decision 판정이 그 matrix row 를 consume.
    • ca-tmpl/docs/registries/error-codes.yaml (owner_branch 다수) — coverage gate 의 입력. registry schema 변경 시 게이트 parser 영향.

Audit & Findings

ca-tmpl ground truth 대조에서 발견한 drift/scope. 사용자 작성 결정 영역은 자동 rewrite 보류 — 정합 권고만(CLAUDE.md §11, §15.5 R3, consistency-contract Single-Owner).

  • COVERAGE_DRIFT (정합 권고) — error-codes.yaml L24-28 의 runbook policy 는 두 절: (1) retryable=false+category∈{6개}⇒runbook 필수, (2) retryable=true 인 모든 row⇒runbook 필수. 본 노트 §Error Registry ↔ Runbook Coverage + D10 은 (1)만 기술, (2)를 누락. → §구현 가이드 §2 에는 (2)를 반영했으나, 사용자 결정 테이블(§Error Registry, D10)은 보존. 권고: D10 + §Error Registry 에 retryable=true 절 추가.
  • RUNBOOK_LINK_RESOLUTION_DRIFT (grep 2026-06-14) — 두 coverage 방향이 공존: (forward) error-codes.yaml 의 per-code runbook:// link 40개 → {area}-{scenario}.md 규칙으로 6건만 resolve / 34 미존재; (reverse, 실제 SSOT) runbook error_codes: frontmatter 10/10 선언 → consolidated 허용. 즉 forward 의 "34 orphan" 은 실제 coverage 미달이 아니라 두 방향의 granularity 불일치. 권고: D11 후보 — forward scheme 을 (a) error_codes: 단일 SSOT 로 수렴(scheme 은 라벨, link-check 는 reverse-index 검사) vs (b) per-code 1:1 파일 분리(40 파일). 실제 구현은 이미 (a) consolidated+frontmatter 채택 → 노트 §1 forward resolver 가정과 정합 필요. 결정은 사용자 영역.
  • OUT_OF_BRANCH_SCOPE — alerting-platform 결정 (D5/D6/D7/D8) — governing doc(observability-log-metric-trace-runbook §Metric)은 alert severity P1/P2/P3 + threshold 를 raw/branch-notes/feature-metrics-alerting-contract 에 위임. 그 branch 가 P1/P2/P3 severity(§P1/P2/P3 정량 기준) + alert payload(D10) 의 owner.
    • D7(P3 정의)·D8(P1 2분 임계) = 그 owner 와 중복 → RESTATED_FOREIGN_DECISION. 권고: reference-only 포인터(feature-metrics-alerting-contract §P1/P2/P3)로 위임.
    • D5(dedup 5분/flapping)·D6(maintenance mute) = alert-routing(Alertmanager silence/inhibition) 영역으로 두 branch 어디에도 owner 없음. 권고: alerting branch 또는 신규 feature-alert-routing-contract 로 이관.
    • 사용자 작성 결정이라 본문(결정 사항·D5~D8 row) 보존 — 이관은 사용자 결정. 본 branch 자동조사에서 D5/D8 fill-here 연구는 보류(out-of-scope 결정을 entrench 하지 않음, R3).
    • 본 branch in-scope runbook 결정 = D1·D2·D3·D4·D9·D10.
  • D1 구현 완료 (2026-06-15 locally-verified)RunbookCoverageContractTest.javasrc/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ 에 신규 생성됨. JUnit 4-test gate(COVERAGE / LINK_FORMAT / LINK_RESOLUTION / PLACEHOLDER_STUB_SMOKE). ./gradlew :app-bootstrap:test --tests '*RunbookCoverageContractTest' PASS. ./gradlew :app-bootstrap:test 전체 PASS. 34개 신규 stub runbook 생성(docs/runbooks/*.md), template.md 신규 생성. STUB_ALLOWLIST 44개(기존 10 + 신규 34). 커버리지: 40 mandatory code 전부 runbook error_codes: frontmatter 에 등록, 39개 runbook:// link 전부 파일 resolve.
  • ca-quality-reviewer fixes applied (2026-06-15 locally-verified) — 3개 수정. (1) Fix 1 (BLOCKING): Test D if (!Files.isDirectory(runbooksDir)) return;Assumptions.assumeTrue(...) 로 교체 — bare return 이 PASS 로 보고되던 것을 SKIPPED 로 수정, 클래스 Javadoc "never silently passed" 계약 이행. (2) Fix 2 (ADVISORY): 미사용 import 2개 제거 — import static org.assertj.core.api.Assertions.fail;, import java.util.LinkedHashMap;. grep 으로 실 사용 없음 확인. (3) Fix 3 (MINOR): extractFrontmatterend <= 4 guard 추가 — 빈 frontmatter(---\n---) 시 StringIndexOutOfBoundsException 잠재 버그 선제 차단. ./gradlew :app-bootstrap:test --tests '*RunbookCoverageContractTest' PASS (4 tests run, BUILD SUCCESSFUL).
  • NO automation yet — D4(link-check)·D10(coverage gate)는 이전에 planned. ca-tmpl 에 runbook-coverage 테스트가 2026-06-15 실 구현됨(actually-implemented). 단 lychee 등 외부 link-checker 통합은 여전히 planned.

테스트 계약

  • alert payload 메타 누락: 모든 P1/P2 alert payload에 다음 5 field가 모두 있어야 함: operation, dependency_name (해당 시), error.code (해당 시), error.category, runbook_link. 측정 방법: Prometheus rule yaml 또는 동등 alert definition 파일을 parse하여 5 field 존재 verify. 1 field라도 누락 시 fail.
  • degrade decision 미정: feature-runtime-health-lifecycle-contract의 Required vs Optional Dependency Matrix에 해당 dependency row가 존재해야 함. 측정 방법: alert가 발생한 dependency_name이 dependency matrix의 row name과 매칭. 미매칭 또는 required column 값이 명시 안 됨이면 fail.
  • runbook orphan alert: 모든 alert definition의 runbook_link field가 runbook://{area}/{scenario} 또는 docs/runbooks/*.md 형식이어야 하고 실제 파일 존재. 측정 방법: alert yaml의 runbook_link → 실제 markdown 파일 path resolve + file exists. 미존재 시 fail.
  • placeholder runbook link: runbook link target 파일 안에 TODO, TBD, PLACEHOLDER 같은 string이 본문에 있으면 fail. 측정 방법: link target 파일을 read → regex (?i)(TODO|TBD|PLACEHOLDER|FIXME) match 시 fail. 또한 link target이 docs/runbooks/template.md로 시작하면 fail (template 자체는 link target 아님).

검증해야 할 주장

Claim Why uncertain How to verify Status
runbook://{area}/{scenario} scheme 이 on-call tool (PagerDuty/Opsgenie) 에서 정상 렌더링 PagerDuty raw Usage Boundary 명시: "보통 https/file URL 만 클릭 가능" PagerDuty 또는 Opsgenie 에서 custom scheme link payload 테스트 needs-confirmation
Error Registry 와 Runbook coverage 가 CI 에서 자동 비교됨 (수동 누락 없음) 자동화 도구의 외부 reference 부재 (D4/D10) CI script 구현 + error registry yaml ↔ runbook file 매칭 테스트 planned
runbook://{area}/{scenario}docs/runbooks/{area}-{scenario}.md resolve 규칙으로 모든 scheme link 가 실제 파일에 도달 grep 2026-06-14: 40 link 중 6 resolve / 34 orphan / 파일 4 unref(consolidated) — resolve 규칙과 실제 파일 granularity 불일치 resolver 구현 + per-code↔consolidated alias 표 결정 후 40 link 전수 resolve 테스트 needs-confirmation
Link-check smoke 가 runbook target 파일 존재 + placeholder string (TODO/TBD/PLACEHOLDER/FIXME) 미포함 검증 link-check 도구 선정 필요 markdown-link-check / lychee 도입 + grep 기반 placeholder 검사 추가 planned
Alert deduplication window 5분, flapping suppression 15분 내 3회 toggle → 30분 mute 정량값이 operator burnout 방지에 효과적 정량값 외부 reference 부재 (D5/D8). OUT_OF_BRANCH_SCOPE — alert-routing owner 에서 검증 Alertmanager silencing 정책 적용 + on-call 회고로 burnout 지표 측정 (alerting branch) planned
ca-tmpl P1/P2/P3 와 PagerDuty High/Medium/Low 매핑이 일관됨 PD-RB-C5 Usage Boundary 명시: "1:1 매핑 보장 안 됨". severity owner = feature-metrics-alerting-contract severity 매핑 표 작성 + on-call SLA 정합 검토 (alerting branch) planned
Alert payload 5 field (operation, dependency_name, error.code, error.category, runbook_link) 가 P1/P2 모두 채워짐 raw 인용 (PagerDuty) 은 alert body description 까지만 보장, 5 field enumeration 은 ca-tmpl 자체 결정 Prometheus rule yaml parse + 5 field 존재 verify (테스트 계약) planned
Maintenance window 시 P2/P3 mute, P1 유지 정책이 incident 누락 없이 동작 maintenance window 정책의 외부 reference 부재 (D6). OUT_OF_BRANCH_SCOPE maintenance window simulation 테스트 + 누락 alert log 분석 (alerting branch) planned
우아한형제들 사례 (장애 유형별 runbook 분리, postmortem→runbook update) 가 본 branch CI gate 와 정합 raw URL 교체 보류 — WW-RB-C5needs-confirmation raw URL 4886 교체 또는 별도 raw 분리 후 verbatim 재인용 + 비교 재작성 needs-confirmation
Atlassian "Runbooks as Code / version-controlled / peer-reviewed" 권고가 ca-tmpl git-hosted markdown 정책 정합 ATL-RB-C5needs-confirmation (원본 URL 404) archive.org 스냅샷 또는 별도 Atlassian 페이지 (handbook chapter) 재확보 needs-confirmation

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

/coverage 가 채우는 생성물 — 손으로 유지하지 않는다. governing 문서(frontmatter governing_docs: wiki/projects/ca-tmpl/observability-log-metric-trace-runbook §Runbook)가 요구하는 관심사를 이 브랜치가 빠짐없이 덮는지의 결과. 기준: rules/coverage-gate.md. 상태: covered-here(이 브랜치 결정) / delegated(다른 owner 브랜치) / missing(아무도 안 맡음 → Blocking).

2026-06-14 coverage-auditor 판정: Covered (missing 0 / Blocking 0 / Should-fix 2 / Advisory 1). governing = observability-log-metric-trace-runbook §Runbook.

관심사 상태 owner 심각도 근거
runbook:// scheme + repo-path 매핑 covered-here OK D3 + §구현 가이드 §1
alert payload 에 runbook_link field covered-here OK D1 + §테스트 계약
link-check / drift 검증 covered-here Should-fix (planned) D4 + §구현 가이드 §3 — 도구 미도입 + stub gap
error-registry ↔ runbook coverage gate covered-here Should-fix (planned) D10 + §구현 가이드 §2 — retryable=true 절 D10 누락(COVERAGE_DRIFT)
runbook 본문 구조 (7-section 표준) covered-here (deferred) — (Phase D2) Should-fix D9 — 본문은 Phase D2; outbox branch 가 D15 로 참조하나 D15 미존재(dangling, Audit)
alert severity P1/P2/P3 정의 + threshold delegated raw/branch-notes/feature-metrics-alerting-contract OK §P1/P2/P3 정량 기준 (governing §Metric 위임)
alert dedup / flapping / maintenance-window mute delegated (owner 미지정) raw/branch-notes/feature-metrics-alerting-contract 또는 신규 alert-routing Advisory D5/D6/D8 OUT_OF_BRANCH_SCOPE (Audit)
custom scheme 외부 채널(PagerDuty/Slack) 렌더링 covered-here Advisory (needs-confirmation) §Claims To Verify — PagerDuty/Opsgenie PoC

완료 후 wiki 추출 대상

  • wiki/projects/ca-skeleton-operational-contract.md의 operational runbook canonical section.

마주친 문제

  • 아직 없음(문서 단계).

묶음

본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.

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

  • (없음 — D1 구현 중 오류 없음. ./gradlew :app-bootstrap:test PASS 확인.)

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

  • (없음 — 별도 추출할 면접 질문 없음. CI gate 설계 패턴은 blog-topics 후보로 충분.)

블로그·채용공고 연계 글감

  • "JUnit 테스트로 운영 runbook coverage gate 구현하기 — Gradle task 대신 테스트를 선택한 이유" (D1 구현 결정 근거: 병렬 feature 간 root build.gradle 충돌 회피)

관련 일일 노트

이 브랜치를 작업한 날짜들. 양방향 nav 유지.

  • (없음 — 캡처 시 추가)

완료 후 정리

머지/종료 시점에 채움.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목:
    • locally-verified 항목:
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned):