콘텐츠로 이동

Source: report/version7/exp-critic/v7_stage05_smoke_infra_review.md

v7 Stage 0.5 Smoke Infrastructure — 적대적 검토 보고서

요약 (Executive Summary)

exp-expert는 단계 0.5 infrastructure 3종 (gate_criteria.md, smoke_analysis.py, v6_baseline_reference.md)을 제출하며 "dry-run의 FAIL은 모두 예상된 failure이므로 실전 smoke에서는 작동할 것"이라 주장한다. 이 주장은 검증되지 않은 기대이며, 본 검토는 다음 구조적 약점을 확인했다:

  • CRITICAL: 병렬 작업 중인 v7_runner.py가 Gate 2/6이 요구하는 param (vq_input_unit, dlinear_output_unit, train_data_hash*, dlinear/vq_scaler_space_signature) 을 로깅하지 않는다. smoke_analysis의 Gate 2는 silent skip, Gate 6는 ERROR 상태로 smoke 통과 차단이 될 가능성이 높다.
  • CRITICAL: Gate 1 dry-run FAIL은 "prereg 대상이라 당연"이 아니라 param key naming 불일치 (definition_hash vs pape_definition_hash). 실제로 prereg 스크립트는 해시 param을 로깅하고 있다 (line 483). smoke_analysis의 query key와 prereg 스크립트 convention이 다르다.
  • MAJOR: --dry-run CLI 플래그가 parser에는 선언됐으나 run_analysis에서 참조되지 않는다 (dead arg). 실전 동작과 dry-run 동작이 코드상 구분되지 않는다.
  • MAJOR: smoke_analysis.py의 PASS 경로 unit test 전무. 합성 MLflow run 기반 end-to-end 테스트 없이 "실전에서 작동할 것"이라는 주장은 근거 없음.
  • MAJOR: Gate 4 SKIPPED → 실전에서 FAIL로 격상되지 않음. 도구가 "조용히 합격" 처리하는 경로 존재.

종합 판정: CONDITIONAL PASS — 아래 CRITICAL 3건 + MAJOR 3건 해소 없이는 실전 smoke에서 유의미한 게이팅이 불가능하다. 단계 0.5 착수 전 재검토 필요.


치명적 문제 (Critical Issues) 🔴

C1. 🔴 Gate 1 param key naming 불일치 — "예상된 실패" 설명의 허위성

Issue: smoke_analysis.py는 pape_definition_hash param을 쿼리하는데 (line 194), stage-0 prereg 스크립트 v7_0419_stage0_preregistration.py는 해시를 definition_hash 라는 다른 key로 로깅한다 (line 483: mlflow.log_param("definition_hash", definition_hash())). expert가 "prereg run에 param이 없는 건 당연"이라 해명한 것은 기술적으로 허위다. param은 존재하며, key 이름만 다르다.

Severity: 🔴 CRITICAL

Evidence: - experiments/federated/v7_0419_stage0_preregistration.py:483mlflow.log_param("definition_hash", definition_hash()) - experiments/federated/v7_runner.py:1643"pape_definition_hash": PAPE_DEFINITION_HASH - experiments/federated/v7_stage05_smoke_analysis.py:194r.data.params.get("pape_definition_hash", "<missing>")

왜 치명적인가: 1. Cycle 1 critic이 요구한 "정의 해시로 drift 차단" 원칙은 key 이름 통일 없이는 무력하다. 이미 별도 memory definition_hash_algorithm_drift.md에 기록된 실패 패턴이 재현되고 있다. 2. expert의 dry-run 결과 해석이 "예상된 실패"로 정리된 탓에, 실전 smoke에서 v7_runner는 pape_definition_hash 로깅 / smoke_analysis는 pape_definition_hash 쿼리로 일치하여 "같은 bug로 인해 통과"할 risk. 그러나 stage-0 prereg run과의 연결이 끊어져 단계 0.5가 단계 0을 검증하지 않는다. 3. "dry-run에서 실패를 감지했다 = 코드가 올바르게 동작한다"는 주장은 FAIL 감지가 의도된 drift인지 naming bug인지 구분 못 한 상태에서 성립 불가.

Resolve 요구: - [ ] prereg 스크립트를 업데이트해 definition_hash와 함께 pape_definition_hash alias 키도 로깅 (backward compat) - [ ] 또는 smoke_analysis가 두 key 모두 fallback 조회 (pape_definition_hashdefinition_hash) - [ ] dry-run 재수행하여 Gate 1이 prereg 3-run에 대해 PASS 나오는 것 확인 → 그때서야 "파이프라인이 작동"이라 말할 수 있음 - [ ] 수정 후 expert 자체 검토 보고서에 "Gate 1 PASS 경로 실증됨"으로 업데이트


C2. 🔴 v7_runner가 Gate 2/6 요구 param을 로깅하지 않음 — contract 불일치

Issue: gate_criteria.md는 engineer가 v7_runner에 다음 param들을 로깅한다고 가정한다: - Gate 2: vq_input_unit, dlinear_output_unit, vq_scaler_space_signature, dlinear_scaler_space_signature - Gate 6: train_data_hash*, val_data_hash*, test_data_hash*

그러나 현재 v7_runner.py는 이 중 어느 것도 로깅하지 않는다. v7_runner.py는 scaler_space_signature (unified) 만 logging (line 1676). VQ/DLinear 분리 signature도, unit string도, split data hash도 코드에 없다.

Severity: 🔴 CRITICAL

Evidence: - experiments/federated/v7_runner.py — grep vq_input_unit → 0 matches - experiments/federated/v7_runner.py — grep dlinear_output_unit → 0 matches - experiments/federated/v7_runner.py — grep train_data_hash → 0 matches - experiments/federated/v7_runner.py:1676 — only scaler_space_signature unified

왜 치명적인가 — 실전 smoke 시 실제 동작 시나리오: 1. Gate 2 (VQ cell A3): smoke_analysis.py 코드 경로 (line 271-281): - s_vq = None, s_dl = None - unified = p.get("scaler_space_signature") → 값 존재 - 결과: WARNING (soft fallback), continue → unit check 건너뜀 - u_vq = None, u_dl = None → line 297 조건 u_vq is not None and u_dl is not None and u_vq != u_dl False → FAIL 아님 → 자동 PASS 경로로 떨어짐 (하지만 WARNING으로 이미 continue로 빠져나옴) - 요약: A3 Gate 2는 WARNING이 되어 overall verdict에 FAIL 신호 못 냄 2. Gate 6: smoke_analysis.py (line 487-499): - any(k.startswith("train_data_hash") for k in r.data.params)False - 결과: ERROR (전 run에 해시 param 없음) - aggregate_overall의 마지막 "Any ERROR → ERROR" 트리거 (line 580) → 전체가 ERROR 3. Gate 2 WARNING + Gate 6 ERROR 조합 → overall은 ERROR 상태, "ALL PASS" 아님 → 단계 1 진입 불가

그러나 더 위험한 시나리오: engineer가 Gate 6 param만 급하게 추가하고 Gate 2 param은 누락한 채 재실행 → Gate 6 PASS, Gate 2 WARNING → overall은 WARNING으로 하강만 하고 FAIL 아님. WARNING은 expert 정책상 "논문 제출 전 수동 검토"라 smoke 통과 가능. 이것이 drift를 silent로 흘려보내는 시나리오다. Gate 2의 전체 의도인 "scaler drift 조기 차단"이 무력화된다.

Resolve 요구: - [ ] engineer에게 v7_runner MLflow 로깅 추가 요구 spec 명시 (param name, 값 형식, 허용값): - vq_input_unit, dlinear_output_unit (cell에 따라 "standardized"/"kW"/"kWh_per_h" 중 하나; VQ cell이면 필수) - vq_scaler_space_signature, dlinear_scaler_space_signature (VQ cell이면 필수; non-VQ는 생략 허용) - train_data_hash, val_data_hash, test_data_hash (스킴 A/B 중 engineer가 택일 후 run param data_hash_scheme에 기록) - [ ] smoke_analysis.py 수정: Gate 2에서 unified fallback이 있을 때 WARNING이 아니라 FAIL로 격상 (soft fallback 제거). VQ cell은 엄격히 split signature 요구. - [ ] smoke_analysis.py 수정: Gate 6에서 "전 run에 해시 param 없음"은 ERROR에서 FAIL로 변경 (engineer 미구현 = 단계 0.5 미통과와 같은 의미; FAIL이 더 정직). - [ ] v7_runner 수정 후 synthetic MLflow run 생성 유닛 테스트로 smoke_analysis PASS 경로 실증 (C3 참조).


C3. 🔴 smoke_analysis.py PASS 경로 검증 없음 — "작동할 것"은 근거 없는 낙관

Issue: tests/ 디렉토리에 smoke_analysis 또는 stage05 관련 테스트가 전무하다. 모든 dry-run 실행이 현재 FAIL 상태이며, "v7_runner 완성되면 PASS 나올 것"이라는 주장은 실증 없음.

Severity: 🔴 CRITICAL

Evidence: - tests/test_*smoke*.py → 없음 - tests/test_*stage05*.py → 없음 - outputs/v7_stage05/smoke_report_*.md 2건 모두 Overall verdict: CRITICAL FAIL - smoke_analysis.py는 총 842 lines — 이 규모의 분석 도구가 unit test 없이 "실전 준비 완료"로 선언됨

왜 치명적인가: 1. PASS 경로의 코드 경로 (line 552-590 aggregate_overall의 "ALL PASS" 반환점)가 한 번도 실행되지 않은 상태. 2. _most_severe, aggregate_overall, _build_matrix 등의 로직이 edge case (12 runs 중 WARNING 3 + PASS 9, G2 SKIPPED + G5 WARNING 1 등)에서 어떻게 분기하는지 예측만 가능. 3. engineer가 v7_runner 완성 후 실전 smoke 실행 시점 → smoke_analysis 버그 발견 → 재smoke 재실행의 risk. 본 단계 0.5는 "본 캠페인 진입 차단"이 목적인데, smoke_analysis 자체가 먼저 smoke 되어야 한다.

Resolve 요구: - [ ] tests/test_v7_stage05_smoke_analysis.py 신설 (최소 6 테스트): 1. test_gate1_all_pass: 합성 run 3개에 pape_definition_hash=8be2bd2f691deed0 설정 → Gate 1 PASS 2. test_gate1_one_drift_fail: run 1개만 다른 hash → FAIL 3. test_gate2_vq_cells_all_pass: A3 run 3개에 vq/dlinear signature + unit 세팅 → PASS 4. test_gate2_unit_missing_should_fail: VQ cell인데 unit param 없음 → FAIL (현재 PASS/WARNING이므로 C2 fix 후 FAIL이 돼야 함) 5. test_gate5_partial_pass_rule: 12-run 중 2개 FAIL → overall FAIL; 1개 FAIL → overall PASS/WARNING 6. test_gate6_hash_mismatch_per_seed: 동일 seed의 cell 간 다른 해시 → FAIL - [ ] aggregate_overall의 결정적 경로 (ERROR 없음, G1 PASS, 전 gate PASS) 합성 데이터로 검증하여 "ALL PASS" 반환 증명 - [ ] 위 테스트 통과 증거를 본 검토 재제출 시 포함


주요 문제 (Major Issues) 🟡

M1. 🟡 --dry-run CLI 플래그가 dead arg

Issue: parse_args--dry-run action=store_true 를 선언하지만 (line 745-748), run_analysis(args)args.dry_run한 번도 참조하지 않는다. 실제로는 --experiment v7-stage0-preregistration 지정 만으로 prereg runs을 대상으로 쿼리하는 것 뿐이고, dry-run과 full-run의 로직 구분이 없다.

Severity: 🟡 MAJOR

Evidence: experiments/federated/v7_stage05_smoke_analysis.py — grep args.dry_run → 0 matches. run_analysis 본문 (line 752-833)에 조건 분기 없음.

Resolve 요구: - [ ] --dry-run flag를 삭제 (unused) 또는 - [ ] dry-run 모드에서 "Gate 3/5/6 ERROR를 FAIL로 승격하지 않음"을 명시적으로 구현 (예: args.dry_run일 때 ERROR return은 SKIPPED 로 relabel). 현재 상태는 "flag 이름만 존재하는 기만"이다.


M2. 🟡 Gate 4 SKIPPED → 실전에서 FAIL 격상 없음

Issue: aggregate_overallg4 == "FAIL"만 check하고 SKIPPED는 통과시킨다 (line 575-577). 실전 smoke에서 다음 시나리오가 조용히 PASS 된다: 1. engineer가 pape_avg/hr_avg/mse_avg metric을 잘못 이름으로 logging 또는 누락 2. generate_figuresif all(np.isnan(values)): continue (line 706-707)로 해당 metric figure가 생성 안 됨 3. figure_paths = []evaluate_gate4 → SKIPPED 4. aggregate_overall은 SKIPPED 무시 → overall = ALL PASS (다른 gate 모두 PASS일 때)

이 시나리오에서는 Gate 3가 먼저 잡아줄 것이지만 (metric 누락 → Gate 3 FAIL), Gate 4 자체가 "metric 있는데 figure 생성 분기가 skip"되는 케이스 (예: --no-figures 실수 사용, 또는 OUTPUT_DIR permission 오류로 savefig 실패 silent catch)를 감지 못 한다.

Severity: 🟡 MAJOR

Evidence: smoke_analysis.py:575-577, smoke_analysis.py:706-707 (NaN skip), smoke_analysis.py:794 (--no-figures 분기)

Resolve 요구: - [ ] aggregate_overall에 "smoke run 수 ≥ 1 AND figure가 0건 생성됨 → FAIL" 로직 추가 - [ ] 또는 evaluate_gate4figure_paths 비어있고 len(source_run_ids) > 0이면 SKIPPED 대신 FAIL 반환 (figure는 실전 smoke에서 필수) - [ ] --no-figures 플래그는 debug 용 only로 명시, 실전 smoke 권장 커맨드에서 제거


M3. 🟡 v6 baseline reference의 N=1 seed가 의사결정에 사용될 risk

Issue: v6_baseline_reference.md 자체는 "sanity 가이드 only, Gate 판정 아님"이라 제한하지만, 제시된 수치 (B0 PAPE=39.62, B1=39.85 등)가 single seed의 single run에서 나온 값이다. "±5" tolerance band를 제안했으나, N=1에서 σ는 미지수이므로 ±5의 근거가 임의적이다. v7 smoke에서 B0 Apt6가 45 또는 55가 나왔을 때 "±5 범위 안" / "밖" 판단을 이 reference로 내리면 single-point reference로 방향 결정하는 것이다.

실제로 보고서 §"Smoke 기대 범위 제안"은 "범위 밖 → WARNING"이라 명시하여 이 reference가 판단 기준에 들어갈 것을 예약한다. memory figure_design_uncertainty_hiding.md 에 기록된 "single-seed/multi-seed 혼재시 uncertainty 은폐" 패턴과 구조적으로 유사.

Severity: 🟡 MAJOR

Evidence: report/version6/exp-expert/v6_baseline_reference.md:43 ("N=1 sample"), :87-92 ("±5 tolerance" 제안), :43 ("IQR 계산 불가")

Resolve 요구: - [ ] 보고서 §"Smoke 기대 범위 제안"을 재작성하여 default를 "reference 사용 금지 — v7 내부 smoke의 seed 간 분산만으로 판정"으로 변경 - [ ] v6 수치는 "절대값이 극단적으로 다른 경우 (예: B0 Apt6가 20 미만 또는 80 초과)"의 sanity trip-wire 정도로 용도 제한 - [ ] "±5 tolerance" 같은 수치 guidance는 N=1에서 근거 없으므로 삭제 - [ ] v6-v7 scaler space 동일성 "값 범위 스캔으로만 추정"의 정확한 기준 명시 (예: "max(y_true) > 5.0이면 inverse-transformed로 추정")


M4. 🟡 Gate 2 silent-skip 경로의 구조적 위험 (C2와 별개 — 수정 후에도 존재하는 취약)

Issue: C2의 fix로 v7_runner가 param을 로깅해도, B0/B2 (non-VQ cells) 에서는 Gate 2가 SKIPPED로 떨어진다 (vq_runs = [] → line 256-260). 이 경로는 "VQ cell 없음이 Gate 2 부적용"이지만, 만약 engineer가 VQ cell을 실수로 use_vq=False로 설정하거나 CELL_REGISTRY lookup이 실패하면 Gate 2가 조용히 건너뛴다.

Severity: 🟡 MAJOR (silent false-negative)

Evidence: smoke_analysis.py:254-260

Resolve 요구: - [ ] Gate 2에 "smoke plan의 cell 목록이 VQ cell을 포함한다고 선언하면 use_vq=True인 run이 존재해야 한다" 제약 추가 (smoke_plan 메타데이터 vs 실제 runs 대조) - [ ] 현재 smoke plan (A3 포함)에서는 use_vq=True run이 최소 seed 수 × households 수 = 6개 나와야 하는데, 이것이 실제 반환되는지 assert


M5. 🟡 Output 디렉토리 retention policy 부재

Issue: 매 smoke_analysis 실행마다 outputs/v7_stage05/smoke_report_{timestamp}.md + smoke_verdict_{timestamp}.json + figures가 누적 생성된다. .gitignoreoutputs/v7_stage05 예외 처리 없음. 이미 2개 stale report 누적 (smoke_report_20260419_224935.md, smoke_report_20260419_225323.md) — 어느 것이 "공식"인지 표식 없음.

Severity: 🟡 MAJOR (reproducibility / 운영 혼란)

Evidence: - .gitignore — grep v7_stage05 → 0 matches - outputs/v7_stage05/smoke_report_*.md 2개 누적

Resolve 요구: - [ ] .gitignoreoutputs/v7_stage05/ 추가 또는 (논문용 reference라면) 최신 1건만 커밋하는 규칙 명시 - [ ] smoke_analysis.py--canonical flag 추가 — 지정 시 최신 symlink 또는 smoke_report_latest.md 생성 - [ ] 또는 single file outputs/v7_stage05/smoke_report.md만 overwrite (history는 MLflow에 이미 있음)


경미한 문제 (Minor Issues) 🟢

m1. 🟢 --run-name-prefix 기본값 불일치

parse_args default는 "v7_" (line 739) — v7_runner의 run_name 규약 f"v7_{cell_id}_seed{seed}_{mode}_{phase}" (line 1620)와 일치하지만, 사용자가 문서 예시를 따라 --run-name-prefix smoke 로 부르면 smoke는 run name 뒤쪽에 있어 매칭 0건. 문서 (docstring line 8)의 예시 --run-name-prefix smoke실제 작동 안 한다.

Resolve: - [ ] docstring 예시 수정: --run-name-prefix v7_ 또는 --run-name-prefix v7_B0 - [ ] 또는 mode substring 매칭 지원 (contains 세미antics 추가)

m2. 🟢 run_ids 메타데이터의 순서 비결정성

_embed_png_metadatarun_ids = ",".join(run_ids) — runs 순서가 MLflow search_runsorder_by=["attributes.start_time ASC"]로 결정. 재실행 시 stable, but 두 engineer가 다른 time zone에서 MLflow tracking 하면 순서 변동 가능. Gate 4는 set 비교이므로 판정은 stable, 하지만 embedded value의 reproducibility는 떨어짐.

Resolve: - [ ] sorted(run_ids) 로 정렬 후 join하여 문자열 결정성 확보

m3. 🟢 evaluate_gate4의 timezone warning 로직의 false positive

line 419: if not (t.endswith("Z") or "+" in t[10:] or "-" in t[10:])2026-04-19T14:23:05-05:00은 OK, 하지만 2026-04-19T14:23:05 (naive)는 "-"가 t[10:]에 있으면 false OK 판정. 실제로는 "T14:23:05"에 - 없어 FALSE → warning 정확. 그러나 edge case 2026-04-19T14:23:05.123 (마이크로초 포함)은 "-"가 position 10 이후에 없어 warning. 포맷 가정이 너무 narrow.

Resolve: - [ ] datetime.fromisoformat(t) parse 시도 후 tzinfo is None이면 warning — 더 robust

m4. 🟢 _most_severe 의 의미론적 혼란

ERROR는 priority=2 (FAIL보다 약함, WARNING보다 강함). 즉 "run-level FAIL이 있지만 다른 run은 ERROR"면 Gate 전체는 FAIL로 판정. 그러나 "전 run ERROR (metric 부재)"는 Gate 레벨에서 ERROR, aggregate에서 line 580 이 catch. 의미 명확하지만 순서에 따른 semantics가 문서화되지 않음.

Resolve: - [ ] gate_criteria.md §7에 "ERROR는 FAIL보다 약한 signal이며 aggregate 단계에서 별도 catch됨" 명시

m5. 🟢 Golden tensor check가 smoke run과 독립적

evaluate_gate1은 smoke run과 별개로 build_golden_tensors()를 호출한다 (line 215). 이는 Python 환경에서 항상 결정적이므로 smoke run의 상태와 무관하게 PASS/FAIL된다. 즉 "smoke run이 모두 FINISHED 아닌 상태에서도 golden tensor만으로 G1 부분 PASS" 가능.

Resolve: - [ ] golden tensor check 결과는 smoke run과 별개 report 섹션에 두고, overall verdict에 반영할지 여부 명시 (현재 PASS면 FAIL과 공존 → _most_severe에서 FAIL 우선, 논리적 OK)


인정되는 강점 (Acknowledged Strengths)

  • gate_criteria.md 문서화 수준: 각 Gate의 pseudocode, Pass/Fail/Warning band, partial-pass 정책이 design spec 추상 문구를 구체화한 점 — 매우 견고한 설계 문서.
  • 공용 모듈 의존 원칙 준수: peak_analysis.v7.metricsdefinition_hash()/build_golden_tensors()를 smoke_analysis가 직접 import하여 정의 drift를 런타임에서 가드 (line 58-62).
  • ERROR vs FAIL 구분 시도: Gate 평가 결과에 "정의상 실패"와 "데이터 부재로 평가 불가"를 별도 상태로 두는 설계는 honest한 판정 구조.
  • Gate 5 partial-pass rule의 균형: 11/12 허용 + "FAIL 2개 이상이면 overall FAIL"은 local optimum vs systemic divergence를 분리하는 합리적 판단.
  • v6 baseline reference의 caveat 섹션: N=1 한계를 명시하고 "reference 사용 금지, Gate 판정 아님"을 강조한 점 — honest.
  • Gate 4 PNG metadata tEXt chunk 해석: PNG에 EXIF가 없다는 기술적 사실을 정확히 반영한 구현.

exp-expert에게 전달하는 필수 수정 사항

즉시 수정 (CRITICAL — 실전 smoke 진입 전 필수)

  1. C1 (Gate 1 key naming): prereg 스크립트 또는 smoke_analysis에 definition_hashpape_definition_hash fallback 추가. 수정 후 uv run python -m experiments.federated.v7_stage05_smoke_analysis --experiment v7-stage0-preregistration 재실행하여 Gate 1 PASS 확인.
  2. C2 (v7_runner param contract): engineer에게 v7_runner.py에 다음 param 로깅 추가 요구. spec 문서를 engineer에게 전달:
  3. vq_input_unit: str ∈ {"standardized","kW","kWh_per_h"} (VQ cell 필수)
  4. dlinear_output_unit: str (동일 허용값, 전 cell 필수)
  5. vq_scaler_space_signature: str (VQ cell 필수)
  6. dlinear_scaler_space_signature: str (VQ cell 필수; non-VQ는 scaler_space_signature unified 허용)
  7. train_data_hash{_{hh}}?, val_data_hash{_{hh}}?, test_data_hash{_{hh}}? (scheme A 또는 B; run param data_hash_scheme: "per_household" | "unified"로 명시)
  8. C2.b (Gate 2 soft-fallback 제거): smoke_analysis.py line 271-281의 unified signature fallback을 WARNING → FAIL로 승격. VQ cell은 split signature 필수.
  9. C2.c (Gate 6 ERROR → FAIL): smoke_analysis.py line 494-499의 ERROR를 FAIL로 변경 (engineer 미구현도 FAIL과 동치).
  10. C3 (unit test 신설): tests/test_v7_stage05_smoke_analysis.py 작성 후 PASS 경로 실증 (최소 6 테스트, 위 C3 섹션 참조).

단계 0.5 실행 전 수정 (MAJOR)

  1. M1 (--dry-run flag): flag 삭제 또는 실제 구현.
  2. M2 (Gate 4 SKIPPED 승격): figure 0건 + runs ≥ 1이면 SKIPPED → FAIL.
  3. M3 (v6 reference): "±5 tolerance" 삭제, "reference 사용 금지 default" 채택. v7 smoke는 seed 3개의 내부 분산으로 판정.
  4. M4 (Gate 2 smoke plan 대조): VQ cell 선언된 smoke plan에서 use_vq=True run 수가 기대치 미달이면 Gate 2 FAIL.
  5. M5 (retention): .gitignoreoutputs/v7_stage05/ 추가, 또는 smoke_report_latest.md canonical symlink 생성.

수정 후 재검토 게이트

위 수정 완료 후 exp-expert가 제출해야 할 재검토 산출: - [ ] PASS 경로 실증: 합성 MLflow run 3개 (B0 2-seed + A3 1-seed, 모두 요구 param 완비)로 run_analysis(args) 실행 → overall_verdict="ALL PASS" 반환하는 console 출력 증거 - [ ] C1 fix 후 prereg dry-run: Gate 1 PASS, Gate 3 FAIL (artifact 없음은 여전) — Gate 3 FAIL이 prereg 특성으로 인한 FAIL 인지 (즉 "prereg run은 y_pred 없으므로 Gate 3 FAIL이 당연하고 기대됨")라는 것이 dry-run 모드에서 suppress되는 것을 실증 - [ ] test 실행 증거: uv run python -m pytest tests/test_v7_stage05_smoke_analysis.py -v 출력 — 6 테스트 전부 PASS


재실험 권고 체크리스트

단계 0.5 smoke 착수 전 반드시 확인:

  • v7_runner.py가 gate_criteria가 요구하는 모든 param을 로깅 (C2 완료)
  • smoke_analysis.py PASS 경로 unit test 통과 (C3 완료)
  • prereg run 기반 Gate 1 PASS 실증 (C1 완료)
  • 수정된 smoke_analysis로 1회 전 gate dry-run 재실행 결과 보고
  • v6 reference 사용 범위 조정 완료 (M3)
  • outputs 디렉토리 retention 정책 결정 (M5)

단계 1 진입 판단 기준 (expert가 수정판 제출 후):

  • overall_verdict == "ALL PASS" (WARNING 포함) AND
  • Gate 1 PASS (12/12 hash match + golden tensor 일치) AND
  • Gate 6 PASS (seed별 hash 일치) AND
  • unit test 6개 통과 증거 첨부

최종 판정

CONDITIONAL PASS — design 품질은 견고하나 구현이 contract를 채우지 못한 상태. 현재 상태로 단계 0.5 smoke를 실행하면 Gate 2/6이 silent-skip 또는 ERROR로 overall을 ERROR로 떨어뜨려 단계 1 진입 불가하거나, 더 나쁘게 는 engineer가 param 일부만 채워 Gate 2가 WARNING으로 통과하여 drift가 silent로 다음 단계로 전파되는 risk.

expert의 dry-run 해석 "모두 예상된 실패"는 (1) C1의 key naming bug를 가렸고, (2) PASS 경로가 한 번도 실행되지 않은 사실을 설명 의무에서 면제하는 rationalization. dry-run에서 FAIL만 봤다 = 코드의 PASS 분기 검증 부재이며, 이는 memory abstract_first_rollback_absence.md에 기록된 "검증 부재를 예상 실패로 prettify"하는 패턴과 구조적으로 동일.

다음 행동: 1. exp-expert가 본 검토의 CRITICAL 5건 (C1/C2/C2.b/C2.c/C3) 에 대해 수정판 제출 2. MAJOR 5건 (M1~M5) 순차 처리 3. 재검토 제출 시 PASS 경로 실증 증거 필수 4. engineer와의 contract는 본 문서 §C2의 param spec을 기반으로 명시화


작성자: exp-critic | 작성일: 2026-04-19 | 대상 cycle: v7-stage-0.5 infrastructure round 1