{
  "schema": "replacement_pr_runner_api_diff_matrix.v1",
  "task_name": "TRACK_F_REPLACEMENT_PR_BASELINE_REAUDIT",
  "ts_kst": "2026-05-21 09:45 KST",
  "mode": "read-only · API surface diff",
  "v1": {
    "path": "utils/replacement_pr_runner.py",
    "lines": 718,
    "blob_sha": "1fa8b2d2d9b25288e7e55152fa73b763aad4551b",
    "lineage": ["task-2510 PR #61 (다그다·루·모리건)", "task-2516+1 PR #67 (루 — circular import 제거 + wiring 활성화)"],
    "class_init_signature": "ReplacementPRRunner(runner=None, *, dry_run=False, repo_dir=None, extra_forbidden_patterns=None, timestamp_provider=None)",
    "primary_entry": "execute(pr_number: int, task_spec: Optional[TaskSpec] = None) -> ReplacementResult",
    "module_level_funcs": ["compare_effective_diff", "detect_forbidden_paths", "assert_no_forbidden_git_flags", "load_task_spec", "_default_runner", "assert_no_cherry_pick", "fetch_pr_metadata", "compute_effective_diff", "detect_contamination", "create_clean_replacement_branch", "transplant_expected_files", "commit_local", "push_branch", "commit_and_push", "precheck_local_replacement_diff", "assert_clean_working_tree", "open_replacement_pr", "post_replaced_comment", "validate_replacement_diff", "build_escalation_packet"],
    "cli": "main(argv) (L700)",
    "escalation_model": "EscalationPacket + last_escalation_packet + build_escalation_packet (Critical 7종 연계)",
    "production_wired": true
  },
  "v2": {
    "path": "anu_v2/replacement_pr_runner.py",
    "lines": 442,
    "sha256": "303d55dee11daa87d4b7d2b3b0fe41b07ee6463be5774241940385244fdcd032",
    "lineage": ["task-2537 (ANU v2 자동화 5 모듈 시리즈 2번째)"],
    "class_init_signature": "ReplacementPRRunner(*, gh_runner, git_runner, audit_writer, audit_root, bot_token_env='BOT_GITHUB_TOKEN', bot_git_name=..., bot_git_email=...)",
    "primary_entries": ["detect_contamination(original_pr_diff, expected_files)", "preserve_original_pr(pr_number)", "create_clean_replacement_pr(...)", "classify_failure(failure)", "build_executor_contract(...)"],
    "dataclasses": ["ContaminationReport", "PreservationRecord", "ReplacementResult", "ReplacementFailure"],
    "constants": ["REPLACEMENT_PR_CREATED", "REPLACEMENT_PR_FAILED", "ORIGINAL_PR_PRESERVED", "CRITICAL_REPLACEMENT_FAILED", "CRITICAL_DIFF_REPLACEMENT_FAILED"],
    "module_level_funcs": ["_now_iso", "_coerce_stream", "_extract_pr_number"],
    "design_principle": "one-way isolation (anu_v2/* only) · callable injection (gh_runner/git_runner/audit_writer)",
    "production_wired": false
  },
  "api_diff_table": [
    {"aspect": "init kwargs", "v1": "runner(positional) + dry_run/repo_dir/extra_forbidden_patterns/timestamp_provider", "v2": "gh_runner/git_runner/audit_writer/audit_root/bot_token_env/bot_git_name/bot_git_email", "compatible": false, "note": "kwargs disjoint — 상호 drop-in 불가"},
    {"aspect": "primary execution", "v1": "execute(pr_number, task_spec) 단일 entry", "v2": "detect_contamination + preserve_original_pr + create_clean_replacement_pr 분리 메서드", "compatible": false, "note": "v1 은 orchestrated execute · v2 는 step별 분리"},
    {"aspect": "side-effect model", "v1": "runner callable (default _default_runner subprocess) + dry_run flag", "v2": "gh_runner/git_runner/audit_writer 3 callable 주입 필수", "compatible": false},
    {"aspect": "contamination detection", "v1": "detect_contamination module func + compute_effective_diff", "v2": "detect_contamination method (set 비교)", "compatible": "partial — 이름 동일 · 위치 다름 (module vs method)"},
    {"aspect": "escalation", "v1": "EscalationPacket + build_escalation_packet (module func)", "v2": "ReplacementFailure + classify_failure → CRITICAL_* 매핑", "compatible": false},
    {"aspect": "module-level utility funcs", "v1": "20개 (compare_effective_diff/detect_forbidden_paths/assert_no_cherry_pick 등 merge_queue 가 import 가능)", "v2": "3개 (private helper)", "compatible": false, "note": "v1 의 8 utility func 가 v2 에 없음 — merge_queue_executor 가 의존"},
    {"aspect": "CLI", "v1": "main(argv) entry (L700)", "v2": "없음", "compatible": false},
    {"aspect": "dataclasses", "v1": "ReplacementResult + EscalationPacket 등", "v2": "ContaminationReport/PreservationRecord/ReplacementResult/ReplacementFailure (frozen)", "compatible": "partial — ReplacementResult 이름 동일 · 필드 다를 수 있음"}
  ],
  "verdict": "v1 ↔ v2 는 SAME_NAME_DISJOINT_API. drop-in shim 불가. v1 superset of v2 도 아니고 v2 superset of v1 도 아님 (서로 다른 설계 철학: v1=orchestrated execute · v2=injectable step methods). v1 이 production wired (merge_queue_executor 가 20 module funcs + execute 의존).",
  "scope_invariants_preserved": [
    "read-only audit only", "코드 변경 0", "PR/merge/credential write 0"
  ]
}
