# [DRAFT — HOLD] task-2725 CRITICAL_GAP_STRUCTURED_CLASSIFIER_REPLACEMENT

> ★ 설계 **초안**. 코드 수정·dispatch·PR 생성 금지(회장 승인 전). PR #169(HOLD_EVIDENCE_PRESERVED)에 추가 commit 금지. replacement PR = origin/main **fresh branch**. task-2724(terminal callback)와 분리.

## 회장 인가 (2026-06-03, LOOP_BOUNDARY → replacement 전환)
critical_gap zero-count 가 같은 `_is_zero_count_context`/severity-count suppression 계열에서 **4회 실패**:
- R1 over-broad FN / R2 word-boundary FP / R3 mixed-count FN / R4(redesign 822c0c0b) **substring FP**(port→support/report/export, count→account/discount/country, CRITICAL/HIGH→HIGHLIGHT/SUPERCRITICAL).
→ regex incremental patch + 같은 PR 누적 수렴 **중단**. 구조화 line classifier 로 **replacement**. 단일소스 실패기록: `memory/events/task-2723.pr169-hold.json`.

## 1. replacement task 초안 (목표)
critical_gap zero-count 판단을 "정규식 suppress 누적" → **구조화 line classifier**로 재설계. 라인 전체 suppress 폐기, **line type 먼저 분류** 후 판정.

## 4. 설계 방식 (구조화 classifier)
`classify_line(line) -> LineType` (우선순위 순):
1. **STRONG_ISSUE_MARKER** (`[CRITICAL]`/`[HIGH]`/`![critical]`/`![high]`/`severity:critical|high`) → `ACTUAL_FINDING_LINE` (최우선).
2. **VULN_DENYLIST** (인증/권한 검증/암호화/패치/수정/조치/해결 없음, `secure_mode/timeout/port = 0`) → `ACTUAL_FINDING_LINE`.
   - ★ 영어 토큰은 **단어경계 `\b` 적용** (`\bport\b\s*=\s*0` — `report=0`/`export=0`/`support=0` 미매칭). 한글은 `\b` 의존 금지(R2 교훈) → 공백/문장부호/lookaround 경계.
3. **severity-count pair 파싱** `parse_severity_count_pairs(line)` → `[(sev,n),...]`:
   - severity 토큰은 **단어경계 `\b` 적용** (`\bCRITICAL\b`/`\bHIGH\b` — `HIGHLIGHT`/`SUPERCRITICAL` substring 미매칭).
   - count 키워드도 `\bcount\b` (`account`/`discount`/`country`/`counter` 미매칭).
   - `any(n > 0)` → `MIXED_COUNT_SUMMARY_LINE` (= suppress 금지, 실제 이슈).
   - `all(n == 0)` → `ZERO_COUNT_SUMMARY_LINE`.
4. **부재형**(숫자 없음): COUNT_ANCHOR(severity/이슈/건수, 단어경계) + ABSENCE(없음/없습니다/없다) → `ZERO_COUNT_SUMMARY_LINE`.
5. else → `NEUTRAL_REPORT_LINE`.
판정: **`ZERO_COUNT_SUMMARY_LINE` 만 suppress**(이슈 후보 제외). 나머지(ACTUAL_FINDING/MIXED_COUNT/NEUTRAL)는 기존 `_detect_critical_lines` 로직대로.

핵심 차이 vs R1~4: "라인에 zero 표현 있으면 suppress" → "라인을 **type 분류** 후 ZERO_COUNT_SUMMARY 만 suppress + 모든 영어 토큰 단어경계 강제(substring 0)".

## 2 & 3. expected_files 확정 후보 (replacement — origin/main fresh branch)
1. `teams/shared/verifiers/critical_gap.py` ← `classify_line` 구조화 classifier 신설 + `_is_zero_count_context` 를 classifier 호출로 대체. **verify() public interface / return schema `{status,details}` 불변**. 기존 화이트리스트/메타라벨/RESOLVED 로직 불변.
2. `tests/regression/test_critical_gap_false_positive_2506.py` ← 4 실패모드 fixture + substring fixture (산출물 5).
- branch: `task/task-2725-dev*` (origin/main HEAD 기준 fresh, PR #169 미접촉).

## 5. PR #169 실패모드 4개 fixture화 (회귀 고정)
| FM | 실패모드 | fixture 입력 | 기대 |
|---|---|---|---|
| R1 | over-broad FN | `- [CRITICAL] 인증 없음` / `- [CRITICAL] port=0` | FAIL |
| R2 | word-boundary FP | `- CRITICAL 총0건` / `- 이슈없음` / `- 이슈없다` | PASS |
| R3 | mixed-count FN | `- CRITICAL: 0, HIGH: 1` / `- CRITICAL 0개, HIGH 2개` | FAIL |
| R4 | substring FP | `- report=0 입니다`(무관) / `- account: 0건`(무관) / `- support=0` / `- HIGHLIGHT: 0` / `- SUPERCRITICAL: 0` | PASS(무관·미탐지) |
+ 정상 zero-count(HIGH/CRITICAL 0건·CRITICAL=0·fresh unresolved=0·신규 HIGH/CRITICAL 없음) PASS + 실제 critical(`[CRITICAL] port=0`) FAIL 보존.
★ 4 실패모드를 **회귀로 영구 고정** → 같은 계열 5번째 재발 방지(회귀가 잡음).

## 6. 자동 dispatch 가능 여부 → **NO (회장 승인 전 dispatch 금지)**
- expected_files(critical_gap.py + test)는 verifier/test 라 **파일 자체는 자동 dispatch 허용 범위**(finish-task/dispatch runtime 아님).
- 그러나 회장 지시: ① replacement 전략 ② **구현 전 설계 검토 선행**(Loki/Maat 또는 Codex) + 4 실패모드 fixture 고정 ③ **회장 승인 전 dispatch 금지**.
- → 1차는 **설계 검토(코드 0)** 만 가능. 구현 dispatch 는 회장 승인 후.

## 7. chair_required_reason
`REPLACEMENT_STRATEGY_AFTER_LOOP_BOUNDARY` — 같은 suppression 계열 4회 실패 후 replacement. 회장이 ① fresh branch ② 설계 검토 선행 ③ 승인 전 dispatch 금지를 명시. 자동 진행 금지, 회장 승인 게이트.

## 진행 방식 (회장 verbatim)
1. PR #169 추가 commit 금지 (HOLD 보존). 2. replacement = origin/main fresh branch. 3. expected_files 후보 먼저 보고(이 문서). 4. 구현 전 설계 검토(Loki/Maat/Codex) + 4 실패모드 fixture 고정. 5. 회장 승인 전 dispatch 금지. 6. task-2724 와 분리.

## 금지
- 코드 수정·dispatch·PR 생성 금지(현재) · PR #169 commit/merge/close 금지 · verify() interface·schema 변경 금지 · 부분문자열 anchor(substring) 금지 · finish-task/dispatch runtime/credential/activation/wake 무관.

## ★ 설계검토 (ANU, 2026-06-03 — 6 recurrence doctrine 반영, 코드 0)

### 6 recurrence_class 적용 (단일소스 feedback_gemini_high_recurring_5patterns_260603)
1. **REGEX_SUPPRESS_OSCILLATION** ← 본 task 의 핵심 동기. critical_gap zero-count 가 4회 진동(over-broad FN/word-boundary FP/mixed-count FN/substring FP) → LOOP_BOUNDARY → **structured line classifier 전환**. regex 누적 incremental 폐기. ★ 이 doctrine 의 1번 실증 사례.
2. **WORKSPACE_ROOT_HARDCODE** — 해당 없음(verifier 는 보고서 텍스트 파싱, path 무관). 단 classifier 가 외부 경로/상수 하드코딩 0 확인.
3. **SAFETY_NET_EXCEPTION** — classifier 가 빈 줄/None/비ASCII/malformed 입력에서 crash 0(예외 안전). `classify_line(None or "")` → NEUTRAL fail-safe. verify() 가 어떤 보고서에도 안 죽어야(QC 게이트 안전망).
4. **TEST_OPERATIONAL_SIDE_EFFECT** — 해당 없음(verifier 테스트는 순수 함수, 운영 side effect 0). 단 test 가 실 파일/cron 미접촉 확인.
5. **STALE_FRESH_REVIEW_CONFUSION** — PR governance 시 fresh head 기준.
6. **GITHUB_THREAD_METADATA_DISTRUST** — Gemini 재리뷰 시 코드 직접 대조(PR #169 4회 진동에서 substring FP 가 graphql fresh/stale 혼선 동반했음).

### ★ 실패모드 4 fixture (영구 고정 — 회귀로 5번째 재발 차단)
| FM | class | fixture 입력(이슈섹션 헤더 아래) | 기대 | 회귀 함수명 후보 |
|---|---|---|---|---|
| FM1 over-broad FN | REGEX_SUPPRESS_OSCILLATION | `- [CRITICAL] 인증 없음` / `- [CRITICAL] port=0` | FAIL | test_fm1_overbroad_fn_vuln_negation |
| FM2 word-boundary FP | REGEX_SUPPRESS_OSCILLATION | `- CRITICAL 총0건` / `- 이슈없음` / `- 이슈없다` | PASS | test_fm2_korean_word_boundary_zero |
| FM3 mixed-count FN | REGEX_SUPPRESS_OSCILLATION | `- CRITICAL: 0, HIGH: 1` / `- CRITICAL 0개, HIGH 2개` | FAIL | test_fm3_mixed_count_detect |
| FM4 substring FP | REGEX_SUPPRESS_OSCILLATION | `- report=0 입니다`(무관)·`- account: 0건`(무관)·`- support=0`·`- HIGHLIGHT: 0`·`- SUPERCRITICAL: 0` | PASS | test_fm4_substring_word_boundary |
+ 정상 zero-count(HIGH/CRITICAL 0건·CRITICAL=0·fresh unresolved=0·신규 HIGH/CRITICAL 없음) PASS + 실제 critical(`[CRITICAL] port=0`) FAIL 보존 + 기존 fp/tp/zc 회귀 무손상.
★ 4 fixture 를 **회귀로 영구 고정** → 같은 suppression 계열 5번째 진동 시 회귀가 잡음(LOOP 종결 보증).

### expected_files 후보 (확정 — origin/main fresh branch)
1. `teams/shared/verifiers/critical_gap.py` ← `classify_line(line)->LineType` 구조화 classifier 신설 + `_is_zero_count_context` 를 classifier 호출로 대체. verify() public interface/return schema `{status,details}` 불변. 기존 화이트리스트/메타라벨/RESOLVED 불변.
2. `tests/regression/test_critical_gap_false_positive_2506.py` ← FM1~4 + 정상 + 탐지력보존 회귀.
- branch: `task/task-2725-dev*` (origin/main HEAD fresh, PR #169 미접촉).

### 설계검토 결론
- **방향 적정**: regex 누적(4회 진동)을 line type 분류로 전환 = doctrine #1 정확 적용. 영어 토큰 단어경계 `\b` 강제로 substring FP(report/account/HIGHLIGHT) 차단, 한글은 `\b` 의존 제거(R2 교훈).
- **위험**: verifier 동작 변경 → QC 게이트 전반 영향. 4 fixture + 기존 회귀 무손상이 안전판. 단 critical_gap 은 CRITICAL 전용 스코프(HIGH-only 미탐지)는 별도 scope decision(backlog E5) 유지 — 이 replacement 에 혼입 금지.
- **chair gate**: 구현 dispatch 는 회장 승인 후. 설계검토(코드 0)까지 완료.

## ★ replacement PR 전략안 (회장 Track C — 구현 금지, 전략 확정용)
1. **PR #169 처리**: `HOLD_EVIDENCE_PRESERVED` 유지. 추가 commit/close/merge 0. 4 진동 증거(head e1f5798b→bf150dfa→a2783b39→822c0c0b) 보존. replacement 머지 후 PR #169 close 는 회장 결정(자동 close 금지).
2. **fresh branch**: `task/task-2725-dev*` = origin/main HEAD(현 6a44d712 또는 그 이후) 기준 fresh. PR #169 브랜치(`task/task-2723-dev2`) 미접촉·미상속.
3. **dispatch 게이트**: 설계검토(이 packet + design-review-packet JSON) → Codex/Gemini 검토 → 회장 승인 → fresh branch dispatch. 회장 승인 전 dispatch 0.
4. **team 선정 후보**: dev2(오딘, critical_gap 원작 — task-2723 종료 후) 또는 alternative resolver(resolver 재투입 제한 doctrine 고려). ★ 회장 승인 시 team 확정.
5. **수렴**: classifier 구현 → 4 FM + 보존 regression PASS → PR 생성 → OWNER /gemini review → CI/Gemini watcher → MERGE_READY_CANDIDATE. merge 회장 승인 전 금지.
6. **사후 정책**(회장 verbatim): replacement 이후에도 같은 zero-count 계열 HIGH/CRITICAL 재발 시 추가 패치 금지 → PR hold 또는 alternative resolver 보고.

## ★ regression plan (확정)
- **신규 회귀(4 FM, 영구 고정)**: test_fm1_overbroad_fn_vuln_negation(FAIL) / test_fm2_korean_word_boundary_zero(PASS) / test_fm3_mixed_count_detect(FAIL) / test_fm4_substring_word_boundary(PASS).
- **정상 zero-count 보존**: HIGH/CRITICAL 0건 · CRITICAL=0 · fresh unresolved=0 · 신규 HIGH/CRITICAL 없음 → PASS.
- **탐지력 보존**: `[CRITICAL] port=0` · 인증/권한/암호화 없음 → FAIL.
- **기존 무손상**: test_critical_gap_false_positive_2506.py 기존 fp/tp/zc 전부 유지.
- **fail-safe(doctrine #3)**: classify_line(None/''/비ASCII/malformed) → NEUTRAL, verify() crash 0.
- smoke: `python3 -m pytest tests/regression/test_critical_gap_false_positive_2506.py -q`. rollback: replacement 단일 PR → revert 1커밋(PR #169 변경 0 격리).

## 상태
DESIGN_REVIEW_DONE / DISPATCH_READY_PENDING_CHAIR — auto_continue_allowed=false. chair_required_reason: REPLACEMENT_STRATEGY_AFTER_LOOP_BOUNDARY. 6 recurrence doctrine + 4 fixture 고정 + expected_files 확정 + replacement PR 전략안 + regression plan + design-review-packet(JSON) 완료. 구현 dispatch 회장 승인 대기. PR #169 HOLD 유지. 코드 0.
