이월 자동화 시드 정리 — Trivy/dependabot/envelope (Critic 자기 모순 검출)
요약Sprint 157~163 누적 이월 시드 중 최우선 3건(자동화 #신규1/#신규2/#신규3) 정리
주요 결정
분할 처리 (4 PR)
Phase A/B/C/D 각 PR 단일 책임 — 회귀 위험 최소화. 단일 PR 압축 회피
Phase A close + Sprint 165 재설계 이월
Critic R1 P1 본질 결함 검출 후 즉시 close, 본질 변경(build job load: true + multi-arch 재설계)을 단일 sprint 압축하지 않음. 우선순위 (서비스 안정성 > 개발 속도) 적용
Critic R1 P2 fix 즉시 적용 (Phase B)
blog Dockerfile 동등 커버리지 추가 — 본 PR의 자기 모순(blog 도 CI 빌드 + Trivy scan 대상이지만 dependabot 미커버) 즉시 해소
envelope 단순화 (Phase C)
group 은 score 정규식 추출 패턴 없음 → 항상 status="failed" 반환 (single envelope 단순화 버전, 일관성 정당화)
PII/secret 모의 raw 토큰 미노출 검증 신규 테스트
test_group_response_fallback_no_raw_exposure 도입 — envelope 의 모든 string 값에 raw 토큰 절대 미노출 명시 검증
목표
- Sprint 157~163 누적 이월 시드 중 최우선 3건(자동화 #신규1/#신규2/#신규3) 정리
- 시드 #신규1 — PR 단계 Trivy scan 활성화 (Sprint 160 교훈 #6 정착 시도)
- 시드 #신규2 — dependabot.yml docker 5건 추가 (Sprint 159 base image 정기 갱신 부재 인과 차단)
- 시드 #신규3 —
_parse_group_responseenvelope 적용 (Sprint 159 single 분석 envelope의 group 회수) - 사용자 직접 UAT 5건 가이드 (Sprint 161~163 신규 시각 검증)
결정
- 분할 처리 (4 PR): Phase A/B/C/D 각 PR 단일 책임 — 회귀 위험 최소화. 단일 PR 압축 회피
- Phase A close + Sprint 165 재설계 이월: Critic R1 P1 본질 결함 검출 후 즉시 close, 본질 변경(build job
load: true+ multi-arch 재설계)을 단일 sprint 압축하지 않음. 우선순위 (서비스 안정성 > 개발 속도) 적용 - Critic R1 P2 fix 즉시 적용 (Phase B): blog Dockerfile 동등 커버리지 추가 — 본 PR의 자기 모순(blog 도 CI 빌드 + Trivy scan 대상이지만 dependabot 미커버) 즉시 해소
- envelope 단순화 (Phase C): group 은 score 정규식 추출 패턴 없음 → 항상
status="failed"반환 (single envelope 단순화 버전, 일관성 정당화) - PII/secret 모의 raw 토큰 미노출 검증 신규 테스트:
test_group_response_fallback_no_raw_exposure도입 — envelope 의 모든 string 값에 raw 토큰 절대 미노출 명시 검증
구현 (4 PR, 32 스프린트 연속 브랜치 규율 준수)
| PR | Phase | 브랜치 | 결과 |
|---|---|---|---|
| #280 | A — PR Trivy 활성화 | chore/sprint-164-pr-trivy-enable | ❌ Close (Critic R1 P1 본질 결함 → Sprint 165 재설계) |
| #281 | B — dependabot docker 6건 | chore/sprint-164-dependabot-docker | ✅ Squash merge b13e6c5 |
| #282 | C — group envelope | fix/sprint-164-ai-analysis-group-envelope | ✅ Squash merge 12eb347 |
| #TBD | D — Sprint 164 ADR | docs/sprint-164-adr | 본 PR (architect + scribe) |
Phase A — PR 단계 Trivy scan 활성화 시도 (close)
- 변경:
.github/workflows/ci.yml:781if: github.ref == 'refs/heads/main' && !cancelled()→if: "!cancelled()" - 목적: PR + main 양쪽 Trivy SARIF 업로드 → PR 보안 회귀 검출 1차 게이트 정착 (Sprint 160 교훈 #6)
- Critic R1 P1 검출 (codex review --commit 1e2e3c2):
Avoid scanning registry tags that PR builds never push (ci.yml:784). On
pull_request, build jobs still usepush: ${{ github.ref == 'refs/heads/main' }}, so they do not publishghcr.io/...:main-${{ github.sha }}. With this job now running for PRs, any changed service that reaches Trivy will try to pull a tag that was never pushed and fail before producing a useful security result. - 본질 결함: PR build는 GHCR push 안 함 → Trivy가 부재 태그 pull 시도 → fail. 본 PR이 보안 게이트가 아닌 noisy CI red 양산 = sprint 목표 자기 모순
- 결정: PR close + Sprint 165 재설계 이월 (build job
load: true+ Trivy local image scan, 또는 PR-specific tag push, 또는 buildx artifact + fs scan 3안 비교 필요) - 학습: 단순 1줄
if변경처럼 보이는 작업도 image registry 정책과 conjunction 시 본질 재설계 필요
Phase B — dependabot.yml docker 6건 추가
- 변경:
.github/dependabot.ymldocker entries 2 → 8- 기존:
/services/gateway,/services/ai-analysis - 신규:
/services/identity,/services/submission,/services/problem,/services/github-worker,/frontend,/blog
- 기존:
- 공통 설정: weekly Monday Asia/Seoul, labels
["dependencies", "docker"], commit prefixchore(docker), semver-major ignore - Critic R1 P2 검출 (codex review --commit cdec0a2, session
019e3f46-4dad-73c0-911f-09066d212f60):Include the blog Dockerfile in Docker updates. CI builds and Trivy-scans a
blogimage from/blog/Dockerfile, but this new Docker coverage still omits/blog. If the blog image'snginxbase tag goes stale or vulnerable, the same Trivy failures this change is meant to prevent can still recur for blog. - 즉시 fix (commit
0f81fb1):/blogdocker entry 추가 — 본 PR 인과 차단 목표와 자기 모순 미커버 해소 - Critic R2 PASS ✅ — "No breaking or actionable issues were identified in the change"
- 머지: squash sha
b13e6c5
Phase C — _parse_group_response envelope 적용
- 변경:
services/ai-analysis/src/claude_client.py:476-490(envelope 패턴 적용)- 기존:
fallback = raw_text.strip()+ backtick 제거 +comparison: fallback[:50000](raw 노출 50KB) - 신규:
comparison: "AI 그룹 분석 결과 파싱에 일시적 오류가 발생했습니다. 잠시 후 다시 시도해주세요."(raw 본문 0 노출) - logger.warning extra:
raw_length만 기록 (raw 본문 미노출) - group 은 score 정규식 추출 패턴 없음 → 항상
status="failed"(single 단순화 버전)
- 기존:
- 테스트 (
services/ai-analysis/tests/test_claude_client.py):- 기존 4건 envelope 검증 전환:
test_parse_group_response_invalid_json+test_parse_group_response_empty_string+test_group_response_fallback_backtick_no_closing+test_group_response_fallback_backtick_with_closing - 신규 1건:
test_group_response_fallback_no_raw_exposure— PII/secret 모의 raw (hunter2/123-45-6789/sk-abc123def456/user_id=42) 입력 시 envelope 의 모든 string 값에 raw 토큰 절대 미노출 검증
- 기존 4건 envelope 검증 전환:
- Critic R1 PASS ✅ (codex review --commit, session
019e3f4a-92d5-7503-8dd3-e92f5ae18c78): "I did not find a discrete regression introduced by this commit" - 검증: pytest 79 PASS, claude_client.py coverage 99% 유지
Phase D — Sprint 164 ADR (본 PR)
docs/adr/sprints/sprint-164.md(KR, 본 파일) +docs/adr-en/sprints/sprint-164.md(EN, 1:1 매핑)docs/adr/README.md갱신 — count 103 → 104, range 62163 → 62164
검증
| 항목 | 결과 |
|---|---|
| Phase B YAML syntax | ✅ docker entries 8건 (2→8) |
| Phase B CI | 29 checks SUCCESS + SKIPPED |
| Phase B Critic R1 P2 → R2 | PASS ✅ |
| Phase C pytest | 79 PASS (group 5건 포함) |
| Phase C claude_client.py coverage | 99% 유지 |
| Phase C Critic R1 | PASS ✅ |
| check-adr-en-coverage --strict | 113/113 (100.0%) PASS (Phase D 후 예정) |
| check-doc-refs | 0 broken refs (Phase D 후 예정) |
| 브랜치 규율 | ✅ 32 스프린트 연속 준수, main 직접 commit 0건, --no-verify 0건 |
브랜치 규율 ✅ 32 스프린트 연속 준수
- 4 PR 모두 신규 브랜치 + Squash merge (Phase A는 close + 브랜치 삭제)
- main 직접 commit 0건,
--no-verify0건
신규 패턴
- Critic R1 자기 모순 검출 2회 단일 sprint 재현 (Sprint 155/159/160 패턴 강화) — Phase A 본질 결함(image tag 부재) + Phase B blog 동등 커버리지 누락. Codex 교차 검증의 sprint 일관성 가드 효과 본 sprint 두 번 재확인
- PR close + Sprint 165 재설계 이월 패턴 — 단순 1줄 변경처럼 보이는 작업도 본질 재설계 필요 발견 시 즉시 close + 별도 sprint 분리. 단일 sprint 압축 회피 (Sprint 161~163의 3 sprint 분할 정책 계승)
- envelope 패턴 cross-service 회수 — Sprint 159 single 분석 envelope을 group 측에 적용. score 정규식 추출 차이로 단순화 (group: 항상
status="failed"). 동일 패턴이 서로 다른 함수에 적용될 때 차이 정당화 필수 - PII/secret 모의 raw 토큰 미노출 명시 검증 — envelope 패턴의 핵심 약속(raw 본문 0 노출)을 단위 테스트로 강제. 회귀 차단 게이트 (Sprint 159 envelope의 부분 검증 한계 보완)
- stash + branch 전환 다중 PR 병행 처리 — Phase B Critic R1 P2 fix 시 Phase C 작업을 stash → Phase B 복귀 → fix push → Phase C stash pop 패턴. 단일 세션에서 다중 PR 진행 시 안전 처리
- codex CLI 직접 호출 백그라운드 —
codex review --commit <sha>를 백그라운드로 실행하여 다음 Phase 진행과 병행. Critic 사이클 처리 시간 hiding