Skip to content

ci(blog): 블로그 cross-check 게이트 (Sprint 177 #18)#307

Merged
tpals0409 merged 4 commits into
mainfrom
feat/sprint-177-blog-crosscheck
May 20, 2026
Merged

ci(blog): 블로그 cross-check 게이트 (Sprint 177 #18)#307
tpals0409 merged 4 commits into
mainfrom
feat/sprint-177-blog-crosscheck

Conversation

@tpals0409

Copy link
Copy Markdown
Owner

변경 내용

블로그 글(KR/EN)을 머지 전 결정론적으로 cross-check 하는 CI hard gate 신규 추가 (Sprint 177 #18 — Sprint 157부터 7스프린트 이월 시드 회수). 의존성 0(node 내장만), 회고록 시점 고정 도메인 사실(스프린트 수·테스트 수·커버리지)은 의도적 제외(false-positive 방지) — 구조적 검증만.

게이트 3축 (scripts/check-blog-crosscheck.mjs, --strict):

  • KR↔EN 정합: slug 양방향 짝(고아 차단) + 구조 필드(date/category/order/tags/series/seriesOrder) 일치 (title/excerpt는 번역 대상 제외)
  • frontmatter 스키마: 필수 필드 + category enum(SSOT posts.ts) + date 형식(YYYY-MM-DD, 비문자열 거부) + order 동일 date 내 유일
  • 내부 링크 무결성: 코드펜스/인라인 코드 제외 후 — 파일시스템 이탈/OS절대 경로 거부 + locale-aware post 라우트(자기 locale 라우트만, fragment/query 정규화 후 슬러그 존재 검증)

dogfood: Critic 교차 리뷰 과정에서 posts-en/toward-model-agnostic-harness.mdx의 실제 locale-leak(/posts/baekjoon-gone/en/posts/baekjoon-gone) 1건 발견·수정.

변경 유형

  • ci/infra: CI/CD 또는 인프라 변경

필수 체크리스트

  • lint 통과 (node 스크립트, ESLint no-console 해당없음 — JSON 아닌 CLI 출력)
  • 커밋 메시지 Conventional Commits 준수
  • 게이트 자체 검증: --strict 현행 콘텐츠 0 위반 exit 0 + tamper 회귀(parity/schema/link 각 위반→exit1, 원복→exit0)

보안 체크리스트

  • 민감 정보 커밋 없음 (.env, 토큰, 키)
  • 로그 출력에 토큰/키/이메일/DB 연결문자열 미포함

신규 산출물 (채택 ≠ 소비)

  • 신규 스크립트 scripts/check-blog-crosscheck.mjs 소비처 명시: .github/workflows/ci.yml quality-docs--strict 스텝 + docs: paths-filter 등록 (blog/content/**/*.mdx 변경 시 자동 트리거)

문서/ADR 변경

  • 본 PR은 ADR 미포함 — sprint-177 ADR(KR+EN)은 머지 후 별도 scribe PR로 기록 예정.

Critic 교차 리뷰 (Codex)

머지 직전 codex review --base main 4라운드:

  • R1 → P2 2건(코드펜스 미제외 false-positive / order 전역유일) 수정
  • R2 → P2 2건(locale 무시 / 비문자열 date 우회) 수정 + EN dogfood
  • R3 → P2 1건(fragment/query 우회) 수정
  • R4 → 0건 (introduced issue 없음, CI 안전) ✅ session 019e44e8-edb2-7ad2-bf1a-be76f5ffaa45

Leo added 4 commits May 20, 2026 19:09
블로그 글(KR/EN)을 머지 전에 결정론적으로 검증하는 CI hard gate 신규 작성.
사람 눈 확인에 의존하던 블로그 정합성 검증을 quality-docs 잡에 자동화한다.

- scripts/check-blog-crosscheck.mjs (신규): 의존성 0(node 내장 모듈 + 경량
  frontmatter 파서). 검증 3축 — ① KR↔EN 정합(slug 양방향 짝 + date/category/
  order/tags/series/seriesOrder 일치, title/excerpt 제외) ② frontmatter 스키마
  (필수 필드/category enum/date 형식/order 유일) ③ 내부 링크 무결성(파일시스템
  이탈·OS 절대 경로 거부 + /posts·/en/posts 대상 존재). 기본/--lint(WARN)/
  --strict(exit 1) 모드. 회고록 시점 도메인 사실은 검증 제외(false-positive 방지).
- dogfood: sliding-window-agent-context.mdx KR/EN 의 파일시스템 이탈 링크
  (../../../../Desktop/...) → /adr/sprints/151/ · /en/adr/sprints/151/ 로 교체.
- ci.yml: docs paths-filter + quality-docs 스텝(--strict) 배선.
Critic(Codex) P2 2건 + dogfood 전제 무효 수정.

- P2-1 (코드펜스 미제외 false-positive): stripCode 헬퍼 신규 — 링크 추출 전
  펜스드 코드 블록(``` / ~~~, info string 포함)과 인라인 코드 스팬(단일/멀티
  백틱)을 제거한다. 코드 영역 텍스트는 렌더 앵커가 아니라 예시이므로 배포
  사이트 404를 만들 수 없다 → 무결성 검사 대상에서 제외.
- P2-2 (order 전역 유일성 false-positive): order 유일성 판정을 locale 전역에서
  "동일 date 내"로 한정(키 ${date}::${order}). getAllPosts(posts.ts)가 date
  내림차순 우선 정렬 후 동일 date 안에서만 order를 tiebreaker로 쓰므로, 다른
  date 글의 order 재사용은 정상이다. 위반 메시지에 date 포함.
- dogfood revert: 대상이던 sliding-window-agent-context.mdx line 239는
  ```markdown 코드펜스 내부의 예시 텍스트였고 실제 404가 아니었다. P2-1
  수정으로 자동 무시되므로 잘못된 전제의 콘텐츠 편집을 main 기준으로 되돌림.

Sprint 177 #18.
Critic R2 P2 2건(false-negative) 수정.

- R2-P2-1 (locale-leak 미검출): classifyLink/checkPostRoute 에 locale 전파.
  글은 자기 locale 의 post 라우트로만 링크해야 한다 — KO 글은 /posts/<slug>,
  EN 글은 /en/posts/<slug>. 교차-locale 라우트는 누수 위반으로 차단하고,
  같은 locale 이면 대상 .mdx 존재까지 검증. /adr/... 등 비-post 루트절대 통과.
  + EN dogfood: posts-en/toward-model-agnostic-harness.mdx 의 [When Baekjoon
    Vanished] 가 KO 라우트(/posts/baekjoon-gone)를 가리키던 실제 누수 →
    /en/posts/baekjoon-gone 으로 교체(KO 글의 동일 링크는 올바르므로 유지).
- R2-P2-2 (비문자열 date 우회): date 형식 검사를 'string AND match' 에서
  'present 한데 (non-string OR not-match)' 로 변경. 경량 파서가 따옴표 없는
  숫자(date: 20260409)를 number 로 파싱해 형식 검증을 통째로 우회하던
  false-negative 차단. undefined 는 필수필드 검사가 담당(중복 미보고).

Sprint 177 #18.
Critic R3 P2 1건(false-negative) 수정.

- R3-P2 (fragment/query 우회): checkPostRoute 가 `$` 앵커 정규식으로
  슬러그를 매칭해, `/posts/missing#section` · `/en/posts/missing?ref=foo`
  처럼 fragment/query 가 붙은 링크를 미매칭→통과시켰다. 그 결과 존재하지
  않는 슬러그를 가진 깨진 in-page 내부 post 링크가 --strict 를 우회했다.
  진입부에서 `href.split(/[#?]/)[0]` 로 path 를 정규화한 뒤 슬러그
  매칭·존재 검증·locale 누수 판정을 수행하도록 변경. 빈 슬러그(/posts/)는
  자연 미매칭으로 통과(현행 동작 유지).

Sprint 177 #18.
@github-actions

Copy link
Copy Markdown

Coverage Report

Coverage directory not found at /home/runner/work/AlgoSu/AlgoSu/coverage — no services changed, skipping coverage gate.

@tpals0409 tpals0409 merged commit adceabf into main May 20, 2026
50 checks passed
@tpals0409 tpals0409 deleted the feat/sprint-177-blog-crosscheck branch May 20, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant