"""얼어붙은 유료 장부를 **다시 채점**한다. ★provider 호출 **0**.

Codex 승인 (2026-08-31): oracle 한 칸을 바로잡은 뒤 재채점. 유료 재구매는
**하지 않는다** — 장부의 응답 3건을 그대로 재사용한다.

## 왜 보낼 길을 아예 없애나

「안 샀다」는 **내가 그렇게 짰다는 믿음**이지 보장이 아니다. 그래서
`cc_runner.replay` 에는 **sender 인자가 없다** — 장부에 없는 호출이 필요하면
그 자리에서 서고, 사는 길이 코드에 존재하지 않는다.

## 무엇을 안 바꾸나

원고 · prompt/schema/pack · raw 장부 · merge 응답과 판정 · 후처리 코드 ·
채점 규칙 · 세계 사실. **바꾼 것은 oracle 한 칸뿐**이고, 옛/새 좌표를 산출에
같이 남긴다.

    python tools/grounding_audit/cc_rescore.py <journal.json> <out.json>
"""
from __future__ import annotations

import hashlib
import json
import sys
from pathlib import Path
from typing import Any, Dict

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "tests"))

from tools.grounding_audit import cc_runner as rr  # noqa: E402
from tools.grounding_audit.call_payload_table import seal_outbound  # noqa: E402

#: 이 재채점이 바로잡은 **oracle 한 칸**. 옛것과 새것을 같이 남긴다.
ORACLE_FIX = {
    "target": "same_name_different_thing",
    "old_at": [[4, "표", 1]],
    "new_at": [[4, "접힌 표", 1]],
    "why": ("팩 계약은 「그 대상 전체를 가리키는 완전한 최소 이름 덩어리」인데 "
            "oracle 이 벌거벗은 「표」로 잠겨 있었다. 4장 원문은 「운전사가 "
            "주머니에서 접힌 표를 꺼낸다」이고 완전한 최소 명사구는 「접힌 표」다. "
            "모델이 계약을 지켰고 oracle 이 계약을 어겼다"),
    "kind": "post-hoc fixture oracle correction",
}


def fixture_sha() -> str:
    from tests.grounding.fixtures import synthetic_episode as ep

    p = Path(ep.__file__)
    return hashlib.sha256(p.read_bytes()).hexdigest()[:16]


def main() -> int:
    if len(sys.argv) < 3:
        print(__doc__)
        return 2
    jp, out = Path(sys.argv[1]), Path(sys.argv[2])
    if out.exists():
        print(f"★{out} 가 이미 있다 — 얼어붙은 산출을 덮지 않는다")
        return 2

    seal_outbound()                       # ★import 뒤에 잠근다
    from tests.grounding.fixtures import synthetic_episode as ep

    journal = json.loads(jp.read_text(encoding="utf-8"))
    # ★★`replay` 는 **보낼 길이 없다** — sender 인자가 아예 없다. 「안 샀다」를
    #  믿음이 아니라 구조로 만든다. 장부에 없는 호출이 필요하면 선다.
    got = rr.replay(jp, world_facts=ep.WORLD_FACTS)

    sc_out = rr.score_run(got)
    rec = {
        "kind": "rescore",
        "note": ("★이것은 **post-hoc development diagnostic** 이다. 실데이터를 "
                 "본 뒤 oracle 한 칸을 바로잡고 다시 채점한 것이라 **독립 "
                 "acceptance/PASS 근거가 아니다**. production 배선·shot-count "
                 "동등성·자동 품질 판정의 근거도 아니다."),
        "oracle_fix": ORACLE_FIX,
        "fixture_sha": fixture_sha(),
        "journal": str(jp.name),
        "nonce": journal.get("nonce"),
        "run_id": rr.run_id(journal["lock"], journal["nonce"]),
        "lock": got["lock"],
        "provider_calls": got["bought"],
        "reused": got["reused"],
        "logical": got["logical"],
        "processing_contract": got.get("processing_contract"),
        "quarantined": got.get("quarantined") or [],
        "score": sc_out,
        "human_review": {
            "required": True,
            "machine_flagged_axes": sc_out["needs_human"],
            "axis_basis": ep.AXIS_BASIS,
            "verdict": None,
        },
    }
    out.write_text(json.dumps(rec, ensure_ascii=False, indent=1),
                   encoding="utf-8")
    print(f"■ 재채점 — provider 호출 **{got['bought']}** · 재사용 "
          f"{got['reused']} · 논리 {got['logical']}")
    print(f"  채점 {sc_out['counts']} · 격리 {len(rec['quarantined'])}")
    print(f"  기계 후보 {sc_out['mechanical_candidate']} · 최종 "
          f"**{sc_out['final_candidate']}**")
    print(f"  사람이 볼 축 {sc_out['needs_human']}")
    print(f"  oracle 정정 {ORACLE_FIX['old_at']} → {ORACLE_FIX['new_at']}")
    print(f"  적었다: {out}")
    print("  ★post-hoc development diagnostic — 독립 acceptance 근거가 아니다.")
    return 0 if sc_out["mechanical_candidate"] else 1


if __name__ == "__main__":
    raise SystemExit(main())
