39 KiB
title: branch / feature-operational-runbook-contract
source_type: branch-note
status: raw
branch: feature-operational-runbook-contract
parent_branch:
related_projects: [ca-skeleton]
governing_docs: [wiki/projects/ca-tmpl/observability-log-metric-trace-runbook]
tags: [branch, ca-skeleton, runbook, incident, operations]
created: 2026-05-22
target_merge:
status_label: in-progress
last_pass: 2026-06-15 (ca-quality-reviewer fixes applied to RunbookCoverageContractTest.java in operational-runbook worktree. 이전: D1 구현 완료 — RunbookCoverageContractTest.java + 34 새 stub runbook + template.md. 2026-06-14 /branch-spec — depth Ready(Blocking 0 / Should-fix 3) + coverage Covered(missing 0). 추가: §구현 가이드(runbook resolver + error_codes: reverse-index coverage gate + link-check smoke) · §엣지·실패·의존 · §Audit & Findings · frontmatter parent_branch/governing_docs · §Coverage. ground-truth(grep): Category.java 10-value enum, runbook error_codes: frontmatter 10/10(=coverage SSOT, forward runbook:// 34 orphan 은 방향 불일치), 10/10 status: stub, error-codes.yaml L28 retryable=true⇒runbook 필수(D10 누락=COVERAGE_DRIFT). 미해소(사용자 영역): D11 runbook granularity(forward scheme vs error_codes SSOT) / outbox branch 의 dangling D15 참조 / D5~D8 alerting = OUT_OF_BRANCH_SCOPE → 위임 권고: raw/branch-notes/feature-metrics-alerting-contract)
id: BR-CA-SKELETON-OPERATIONAL-CONTRACT-031
kind: project-work-item
project: ca-skeleton-operational-contract
work_item: WI-CA-SKELETON-OPERATIONAL-CONTRACT-031
inherits: [DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1]
refines: []
overrides: []
depends_on: []
contract_packet: 1
contract_packet_sha256: b942acb6eac3400b82ecca38728ac3f708fb6ab615d362d06f5927cb941075cb
branch: feature-operational-runbook-contract
Layer:
raw/branch-notes/— 장애 알림 이후 운영자가 확인하고 판단할 기준을 runbook 계약으로 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
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 mute — raw/branch-notes/feature-metrics-alerting-contract 가 owner (governing doc §Metric 위임). 본 branch 는 runbook 계약(scheme/link-check/coverage)만. 노트 내 잔존 D5~D8 은
## Audit & Findings의OUT_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)
채택 결정 + 뒷받침
- 결정:
runbook://{area}/{scenario}scheme + repo-relativedocs/runbooks/*.md허용 + link-check smoke + Error Registry ↔ Runbook coverage CI gate. - 뒷받침 source:
- raw/official-docs/runbook-pagerduty-incident-response-doc.md — PagerDuty 권장 runbook 5단 구조(Purpose / Severity / First check / Mitigation / Escalation)와 ca-tmpl Runbook Link Contract required metadata가 1:1 매핑.
- raw/company-tech-blogs/runbook-atlassian-gitops-runbook-as-code.md — git-hosted markdown runbook이 Confluence wiki 대비 drift 적고 PR review 가능. ca-tmpl의 repo path 허용 결정 정합.
- raw/company-tech-blogs/runbook-woowahan-incident-techblog.md — 국내 사례. 장애 유형별 runbook 분리 + alert 생성 시 runbook 동시 작성 원칙이 ca-tmpl coverage CI gate와 정합.
검토 대안 + source
- 대안 1 — Confluence/wiki SaaS runbook: raw/company-tech-blogs/runbook-atlassian-gitops-runbook-as-code.md에서 drift / login 차단 / version 없음 단점 명시. ca-tmpl forbidden.
- 대안 2 — PagerDuty Runbook Automation / auto-remediation: raw/official-docs/runbook-pagerduty-incident-response-doc.md. mitigation 자동 실행 가능하나 vendor lock-in + mutation risk. ca-tmpl out-of-scope (Phase D2 이후 여지).
비교 핵심 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 repodocs/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-contract 의 required 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-foundation의src/shared-contract/.../error/Category.java(10-value, 본 branch 는 consume only).
1. runbook_link 해석 규칙 (resolver)
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-rejected→docs/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.yamlL24-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→codes —
docs/runbooks/*.md10개 전부 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-coderunbook://link 를 forward resolve(§1)하는 방식이 아님. 이 reverse-index 가 consolidated(many-codes→one-runbook)를 자연히 허용해 §엣지의 granularity 문제를 설계상 해소.
3. link-check / placeholder smoke
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/*.md10개 전부status: stubfrontmatter — 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 게이트 코드.
Runbook Link Contract
| 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 는 runbookerror_codes:frontmatter(many-codes→one-runbook, §구현 가이드 §2) → 이 방향이면 정상. 남은 결정: forwardrunbook://scheme 을 유지(유지 시 alias/redirect 필요) vserror_codes:frontmatter 단일 SSOT 로 수렴 — D11 후보(사용자 영역, AuditRUNBOOK_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).
- Orphan runbook link (34건) —
- 다른 계약 의존:
- raw/branch-notes/feature-operational-error-observability-foundation 의
Category.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 영향.
- raw/branch-notes/feature-operational-error-observability-foundation 의
Audit & Findings
ca-tmpl ground truth 대조에서 발견한 drift/scope. 사용자 작성 결정 영역은 자동 rewrite 보류 — 정합 권고만(CLAUDE.md §11, §15.5 R3, consistency-contract Single-Owner).
- COVERAGE_DRIFT (정합 권고) —
error-codes.yamlL24-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) runbookerror_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.java가src/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_ALLOWLIST44개(기존 10 + 신규 34). 커버리지: 40 mandatory code 전부 runbookerror_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):extractFrontmatter에end <= 4guard 추가 — 빈 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과 매칭. 미매칭 또는requiredcolumn 값이 명시 안 됨이면 fail. - runbook orphan alert: 모든 alert definition의
runbook_linkfield가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-C5 가 needs-confirmation |
raw URL 4886 교체 또는 별도 raw 분리 후 verbatim 재인용 + 비교 재작성 |
needs-confirmation |
| Atlassian "Runbooks as Code / version-controlled / peer-reviewed" 권고가 ca-tmpl git-hosted markdown 정책 정합 | ATL-RB-C5 가 needs-confirmation (원본 URL 404) |
archive.org 스냅샷 또는 별도 Atlassian 페이지 (handbook chapter) 재확보 | needs-confirmation |
관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
/coverage가 채우는 생성물 — 손으로 유지하지 않는다. governing 문서(frontmattergoverning_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.
마주친 문제
- 아직 없음(문서 단계).
묶음
- raw/company-tech-blogs/metric-toss-payments-alert-severity-techblog
- raw/company-tech-blogs/runbook-atlassian-gitops-runbook-as-code
- raw/company-tech-blogs/runbook-woowahan-incident-techblog
- raw/official-docs/google-sre-workbook-on-call-monitoring
- raw/official-docs/lychee-link-checker
- raw/official-docs/prometheus-alertmanager-silences
- raw/official-docs/runbook-pagerduty-incident-response-doc
본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
오류 기록 (본 feature 작업 중 발생)
- (없음 — D1 구현 중 오류 없음.
./gradlew :app-bootstrap:testPASS 확인.)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — 별도 추출할 면접 질문 없음. 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):