Sprint 146 회귀 차단 후속 확장 — Panel title + Variable usage 검증
Sprint 147 — Sprint 146 회귀 차단 후속 확장 — Panel title + Variable usage 검증
컨텍스트
Sprint 146에서 scripts/check-grafana-metrics.mjs를 553라인으로 확장하여 dashboard expr의 metric name + label name 정합성을 service code SSOT 기준으로 자동 검증하는 인프라를 구축했음 (PR #209). 본 스프린트는 동일한 entry point에 검증 차원을 누적하는 Sprint 146 패턴을 그대로 계승하여:
- Panel title ↔ metric 정합성: panel 이름 리팩토링 시 expr 미갱신 회귀를 사전 차단 (예: "Circuit Breaker State" panel 유지 + expr만
algosu_*_http_requests_total로 교체 시 검출) - 사용되지 않는 dashboard 변수 검출: 변수 정의 후 expr 삭제 시 발생하는 고아 변수를 사전 차단
- ADR housekeeping: git tree에 누적된 Sprint 144~146 untracked ADR 부채 일괄 머지
이월 시드 2건(시드 #5 UAT 프로그래머스 / 시드 #9 UAT 시각 정합)은 본질적으로 사용자 환경 의존이라 Oracle 작업 대상 외 — Sprint 143~147 누적 이월. Sprint 146의 "UAT → 자동 검증 구조 전환" 패턴대로 회귀 시드는 본 스프린트로 차단망 추가 완료.
3 PR 분할 패턴(Sprint 141 그룹 분할 직접 적용)으로 squash merge 종료.
결정
1) Scope 결정 (B+ADR housekeeping = C안)
- 사용자 선택: C안 (Panel title + Variable usage + ADR housekeeping)
- 후보 D안(+ Recording rule 라벨 검증)은 현재 결함 0건 + ROI 낮음 → Sprint 148+ 시드로 보류
2) Panel title ↔ metric 매칭 알고리즘 = Keyword whitelist
- 사용자 선택: 명시적 keyword 사전(
PANEL_TITLE_KEYWORD_MAP) 기반 매칭 - 대안 (Substring lax / Panel-level annotation)은 false positive 위험 또는 마이그레이션 비용 큼
- 신규 panel 추가 시 본 SSOT를 명시적으로 확장 의무 — JSDoc 주석으로 명문화
3) Plan의 "scribe" 매핑 → 실제 "architect" 재라우팅
- 초기 plan은 PR #1/#2를 scribe에 위임했으나 Scribe가 즉시 거부 ("문서/메모리/Skill만 담당, 코드 작성 금지")
- 재라우팅:
scripts/check-grafana-metrics.mjs는 CI 파이프라인 + Grafana monitoring 도메인 → Architect (.claude/commands/agents/architect.md의 "GitHub Actions CI 파이프라인 + Prometheus/Grafana" 책임) - 결정: plan의 에이전트 매핑은 도메인 매뉴얼 cross-check 필수. 향후 plan 단계에서
.claude/commands/agents/{name}.md의 책임 범위를 명시적으로 확인.
4) Critic 호출 정책
- PR #1: Auto-Critic 2회 (R1 + 재commit 후 R2 = 사실상 R2 강제 호출) — 정규식 매칭 + 신규 SSOT 도입으로 다중 라운드 정당화 (Sprint 146 학습)
- PR #2: 초기 plan은 "Critic 미호출" 명시였으나 Auto-Critic이 자동 큐잉 → P2 1건 적발. 즉시 fix + Auto-Critic R2 클린 통과
- PR #3: 코드 변경 0건 → Critic 미호출 (Sprint 141 동일 정책)
5) DIRTY mergeStateStatus 동시 해소 정책 (신규)
- PR #2 (PR #219)는 첫 push 시
mergeStateStatus: DIRTY발생 (PR #218 머지로 main 갱신 → 브랜치 stale) - 결정: Critic R1 P2 fix와 main rebase를 단일 dispatch로 묶어 cycle time 단축. force-with-lease push로 PR 갱신.
패턴
회귀 차단 본질의 누적 차원 확장 (Sprint 145~146 패턴 계승)
- 단일 entry point
check-grafana-metrics.mjs에서 검증 차원 누적:- Sprint 145: metric name (service code ↔ dashboard expr)
- Sprint 146: label name (TS labelNames + Python labelnames ↔ dashboard expr labels)
- Sprint 147: panel title + dashboard variable
- 결과: 553라인 (Sprint 146 end) → 741라인 (시드 #1) → 823라인 (시드 #2). CI job 추가 부담 0 (
quality-monitoringjob 그대로).
Plan 가정 깨짐 즉시 보고 + 재라우팅
- Sprint 146에서 도입한 "탐색 단계에서 plan 가정 깨졌을 때 진행보다 보고 우선" 원칙을 Scribe 도메인 거부 시점에 적용. 즉시 architect로 재라우팅 결정 → 머지 cycle 영향 0.
- 일반화: 에이전트가 작업을 거부할 때(
status: failed로 inbox 회신) Oracle은 (a) 도메인 재라우팅 (b) 작업 범위 조정 (c) 사용자 결정 중 선택. 본 스프린트는 (a) 선택.
Critic 다중 라운드 R2 강제 → R3 미호출 임계값
- Sprint 146 패턴 ("R2 강제 호출, R3는 P2 잔존 시")을 PR #1에 적용
- R2 Codex 판정: "no discrete regression introduced" → R3 미호출 (R3 클린 임계값 = "discrete introduced bug 없음")
- 임계값 명시화: R2 결과가 "신규 도입 결함 없음 + 기존 결함 모두 해소" 두 조건 동시 충족 시 R3 미호출
Regex 강건성 P2 패턴 누적 (Sprint 145 → 147)
- Sprint 145 P2: dashboard regex
__name__=~selector 통째 마스킹 → false negative - Sprint 146 P2:
5[0-9]{2}quantifier가 selector wrapper 정규식 끊음 - Sprint 147 P2-2:
/algosu:[a-z_:]*availability|success_rate/연산자 우선순위로success_rate가 prefix 없이 단독 매칭 → future false negative - 공통 패턴: PromQL/dashboard 정규식 작성 시 (a)
|우선순위 (b) character class 일관성 (c) quantifier 처리 (d) prefix anchoring 4가지를 매번 점검. 시드 누적 → Sprint 148+ "regex 강건성 lint 룰" 고려 가능.
검증 대상 외 panel의 silent skip 정책 + JSDoc 주의 명문화
PANEL_TITLE_KEYWORD_MAP에 등록되지 않은 panel은matchedKeywords.length === 0조건으로 silent skip- 이 정책의 함정: SLO dashboard "Claude API Request Rate" panel처럼 실제
algosu_*metric을 참조하지만 keyword 미등록 시 검증 대상 외로 빠짐 → Critic R1에서 즉시 적발 + 'request rate' 추가 fix - 신규 panel 추가 시 본 SSOT를 명시적으로 확장 의무를 JSDoc 주석으로 명문화 (운영 문서화)
Grafana 포맷 구문 인식 정규식 (PR #2 P2)
- Grafana multi-value 변수는
${service:regex}/${name:pipe}/${name:csv}포맷으로 panel expr에 주입 extractVariableReferences()정규식에(?::[^}]*)?optional capture 추가하여 colon + format specifier 부분 인식- 현재 dashboard 3개에서 미사용이지만 향후 도입 시 false positive 차단
교훈
1. Plan의 에이전트 매핑은 도메인 cross-check 필수
- 본 스프린트 plan은 "scribe"에 PR #1/#2를 위임했으나 Scribe는 코드 작성 금지가 명시된 도메인
- 향후 plan 단계:
.claude/commands/agents/{name}.md의## 역할 & 핵심 책임+## 금지 사항섹션 확인 의무 - 회귀 차단: plan 작성 시 에이전트 도메인 매뉴얼 1줄 인용
2. Auto-Critic은 plan의 "Critic 미호출" 명시와 무관하게 자동 큐잉됨
- PR #2 plan은 "Critic 미호출"이었으나 code-changing 에이전트가 commit하면
oracle-auto-critic.sh가 자동 트리거 (_base.mdSprint 117~ 정책) - 결과적으로 P2 1건 적발 + 즉시 해소 → 회귀 차단 본질에 이로움
- 교훈: plan의 Critic 호출 정책은 "수동 R2 추가 호출 여부"만 영향. Auto-Critic은 기본값으로 모든 code-changing 작업에 적용.
3. DIRTY merge state는 단순 base mismatch만 의미하지 않음
- PR #219 DIRTY는 base가 main 자체로 따라가지만 브랜치 stale (PR #218 머지 결과 미반영)
- gh API는
baseRefOid: be76c43(main 최신)을 정확히 표시하나 mergeable 계산은 브랜치 stale 감지 - 교훈: 연속 PR 분할 시 N+1번째 PR은 N번째 머지 후 main rebase 필수. P2 fix와 함께 묶어 dispatch하면 cycle time 절약.
4. 회귀 시드 누적 → CI 자동화 후보 식별 신호
- Sprint 144 PR #205 (mock coverage CI script): Critic 적발 패턴 → 자동화
- Sprint 145
146 PR #207209 (prometheus + grafana 검증): 누적 시드 → 자동화 - Sprint 147 신규 시드 후보: regex 강건성 P2가 3 스프린트 연속 발견 → Sprint 148+ "regex 강건성 lint 룰" 또는 정규식 작성 가이드 RUNBOOK 후보
시드 (Sprint 148+ 이월)
UAT 사용자 직접 (Sprint 143~147 누적, 본 스프린트 작업 외)
- 시드 #5: 프로그래머스 재제출 채점 통과 확인 (사용자 직접 UAT)
- 시드 #9: 영문 환경 + production Grafana CB dashboard ai-analysis 시각 정합 확인 (사용자 직접 UAT)
- 회귀 시드는 본 스프린트와 Sprint 146 자동 차단망으로 차단 완료. UAT 자체만 사용자 책임.
자동화 후보 (Sprint 148+ Oracle 작업 대상)
- 신규 시드 #11: Recording rule (
algosu:*) 라벨 정의 ↔ service code 정합 검증 (현재 결함 0건, 선제적, 난이도 Low) - 신규 시드 #12: Dashboard datasource 일관성 + 빈 panel + 중복 panel id 검증 (현재 결함 0건, 선제적)
- 신규 시드 #13: Regex 강건성 lint 룰 또는 정규식 작성 가이드 RUNBOOK (Sprint 145~147 P2 3건 누적 패턴 차단)
- 신규 시드 #14: ai-analysis problem context 후속 (Sprint 143 PR #200 미해소 — frontend에서 problem context 활용 / submission 응답 schema 확장 / saga payload 흐름 검증)
검증
- 모든 PR: CI 28 SUCCESS / 11 SKIPPED, mergeStateStatus CLEAN (force-with-lease push 후 재검증)
scripts/check-grafana-metrics.mjsbaseline 최종: 204 metrics / 32 strict / 15 wildcard / 124 labels / 41 panel title pairs / 2 vars / 0 unused- 회귀 시나리오: PR #1 4건 (시나리오 1: title 유지 + expr metric prefix 변경 / 시나리오 2: title-expr 도메인 mismatch / 시나리오 3: row type skip / 시나리오 4 Critic R1 후속: Claude API Request Rate panel TYPO expr 검출) + PR #2 1건 (templating.list unused_test_var 추가 → FAIL 검출) 모두 정상
- 브랜치 규율: 13 스프린트 연속 준수 ✅ — 3 PR 모두 신규 브랜치 + Squash merge, main 직접 commit 0건 (Sprint 134 위반 이후)
ADR 참조
- 본 ADR:
docs/adr/sprints/sprint-147.md - Sprint 146 ADR:
docs/adr/sprints/sprint-146.md(회귀 차단 자동화 직접 확장 대상) - Sprint 145 ADR:
docs/adr/sprints/sprint-145.md(prometheus/grafana 검증 인프라 기반) - Sprint 141 ADR:
docs/adr/sprints/sprint-141.md(그룹 분할 PR 패턴 직접 적용)