# -*- coding: utf-8 -*-
"""anu_v3.cancel_on_success_live_adoption_2604 — CANCEL_ON_SUCCESS LIVE ADOPTION.

task-2604 Track A (회장 5-track 배치, 독립 task — task-2553 +N 아님).
Strict-additive 신규 orchestration. 기존 frozen 모듈 **read-only consume**
(byte-0 무수정). 신규 cron/cancel 로직 0 — +45/+9a/+23 권위 그대로 경유.

목표 (회장 verbatim §1):
  normal callback durable-success 이후 bound fallback 을 live verifier
  5조건으로 안전 확인하고, 실제 cancel/remove 또는 명시적 NON_BLOCKING /
  CANCEL_FAILED_CLASSIFIED 로 처리한다.

필수 (회장 verbatim §2):
  bound fallback 식별 · live verifier 5조건 · unrelated cron 제거 방지 ·
  cancel-audit JSON · normal success decouple · remove 실패 안전 분류.

구현 방향 (회장 §3 — read-only consume):
  본 orchestration 은 기존 frozen 모듈을 **read-only import 만** 하여 결선:

    * utils/completion_callback_fallback_cancel.py   (+9a cancel·분류)
    * utils/live_cron_state_verifier.py              (+23 5조건 AND)
    * utils/completion_callback_operational_cancel_seam.py (+23 seam)
    * anu_v3/cancel_on_success_live_wiring.py        (+45 durable lookup)
    * anu_v3/cancel_on_success_live_e2e.py           (+48 — 미사용 import 0)
    * anu_v3/callback_4tuple_registry.py             (+44)
    * memory/events/callback_4tuple_index.jsonl      (durable·track 미소유)

  절차:
    1. durable 4-tuple registry(+44) 에서 normal-callback durable-success
       **1건 식별** (classify == NORMAL_CALLBACK_COMPLETED · 최신 record
       status==COMPLETED).
    2. bound fallback_callback_cron_id **안전 lookup** — +45
       ``lookup_fallback_from_durable_registry`` (LOOKUP_VERIFIED 만 진행).
    3. **unrelated cron 제거 방지** — task_id + cron_id 4-tuple ownership
       일치 교차검증(durable record task_id/chat_id/role/fallback_cron_id
       == 처리 대상). 불일치 → NON_BLOCKING (실 remove 0).
    4. +45 ``run_cancel_on_success_live_wiring`` **경유** (PRIMARY) →
       +37→+25→+23 seam(operational) → live verifier 5조건 AND →
       +9a remover 1건.
    5. 결과를 task-2604 채택 분류로 사상:
       ① 5조건 통과 + 실제 cancel/remove(또는 idempotent already-gone/
          fired) → ``CANCEL_ADOPTED``.
       ② remove 실패(+9a REMOVE_FAILED_WARNING) → ``CANCEL_FAILED_
          CLASSIFIED`` (안전 분류 — normal success 무효화 0).
       ③ verifier 미충족 / durable-success 미식별 / lookup 불가 /
          ownership mismatch → registry ``NON_BLOCKING`` 명시 마크
          (fallback 은 안전망·진행 트리거 아님 — 보존이 비차단).
    6. normal success 판정은 cancel 결과와 **decouple** —
       ``normal_success_unchanged`` 절대불변 True. 채택 분류·remove
       실패·skip·예외 무엇이든 durable_success 무변.
    7. 전 결과 ``memory/events/task-2604.cancel-adoption-audit.json``
       (다중 후보 보장 기록 — +45 ``guaranteed_write_audit`` 재사용).

금지 (회장 §5 / HOLD §6):
  기존 task-2553 산출물·+9a/+23/+25/+44~+55·frozen anchor byte-0 ·
  callback_4tuple_index.jsonl track 직접 write 0(read-only consume·
  +53/+54 소관 공유 durable 인프라) · unrelated cron 제거 0 · PR/branch/
  main write 0 · self-callback/self-collector/self-adjudication/self-
  dispatch 0 · ANU authoritative 약화 0 · fallback/dead-man/fixed-time
  진행트리거 0 · 문서-only 0(실 entrypoint+regression·mock-only FAIL).
"""
from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Callable, List, Optional, Union

# +46 canonical-first artifact root (read-only resolve).
from anu_v3.artifact_root_resolver import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    canonical_root,
)

# +44 durable append-only 4-tuple ledger (read-only query 만 — append 0).
from anu_v3.callback_4tuple_registry import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    NO_LEDGER_RECORD,
    NORMAL_CALLBACK_COMPLETED,
    Callback4TupleRegistry,
    default_ledger_path,
)

# +45 cancel-audit 보장 writer (Layer A — NO-CRON, read-only consume).
from anu_v3.cancel_audit_writer import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    guaranteed_write_audit,
)

# +45 cancel-on-success live wiring 진입점 (PRIMARY — 신규 cancel 로직 0).
from anu_v3.cancel_on_success_live_wiring import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    LOOKUP_VERIFIED,
    CancelOnSuccessLiveResult,
    run_cancel_on_success_live_wiring,
)

# 소유권 하드 경계 (+23 verifier 와 동일값 — single definition).
from utils.live_cron_state_verifier import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    ANU_CHAT_ID,
    FALLBACK_ROLE,
)

# frozen anchor — READ-ONLY 타입 only (collector 호출은 +45 가 수행).
from utils.anu_delegation_completion_callback import (  # pyright: ignore[reportMissingImports]  # noqa: E501
    CallbackInput,
    Classification,
    PostResultReview,
    no_real_codex_post_result_review,
)

TASK_MARKER = "task-2604"
ADOPTION_AUDIT_SCHEMA = "task-2604.cancel-on-success-live-adoption-audit_v1"

# ── 채택 분류 (회장 §1/§3 — exactly 3 terminal outcomes) ──────────────────────
#: 5조건 통과 + 실제 cancel/remove(또는 idempotent already-gone/fired).
ADOPT_CANCEL_ADOPTED = "CANCEL_ADOPTED"
#: remove 시도했으나 실패 — 안전 분류. normal success 무효화 0.
ADOPT_CANCEL_FAILED_CLASSIFIED = "CANCEL_FAILED_CLASSIFIED"
#: verifier 미충족 / 미식별 / lookup 불가 / ownership mismatch → 보존(비차단).
ADOPT_NON_BLOCKING = "NON_BLOCKING"

# ── 식별 / ownership 단계 사유 코드 ───────────────────────────────────────────
IDENT_DURABLE_SUCCESS = "NORMAL_CALLBACK_DURABLE_SUCCESS"
IDENT_NO_LEDGER_RECORD = "NO_LEDGER_RECORD"
IDENT_NOT_COMPLETED = "NOT_COMPLETED_PENDING"
IDENT_TRACK_MISMATCH = "TRACK_MISMATCH"


def _now_utc() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


# ── 식별 + ownership 교차검증 결과 ────────────────────────────────────────────
@dataclass
class DurableSuccessIdentification:
    """durable 4-tuple registry 에서 normal-callback durable-success 1건 식별
    + unrelated cron 제거 방지용 4-tuple ownership 교차검증.

    ``identified=True`` AND ``ownership_all_satisfied=True`` 일 때만 실제
    cancel 진행. 그 외 모든 경로 → cron-remove 0, fallback 보존(NON_
    BLOCKING). durable record 의 task_id / chat_id / role / fallback_cron_id
    가 처리 대상과 정확히 일치해야 한다(타 task cron 미인용 — 회장 §2).
    """

    identified: bool
    ident_status: str
    ledger_classification: str
    record_status: Optional[str]
    task_id: str
    bound_fallback_cron_id: Optional[str]
    # ownership 4-tuple 교차검증 (unrelated cron 제거 방지).
    own_task_id_match: bool = False
    own_chat_id_owned: bool = False
    own_role_fallback: bool = False
    own_cron_id_bound: bool = False
    ownership_all_satisfied: bool = False
    chat_id: Optional[str] = None
    role: Optional[str] = None
    ledger_path: Optional[str] = None
    canonical_root: Optional[str] = None
    reason: str = ""

    def to_dict(self) -> dict:
        return {
            "schema": "task-2604.durable_success_identification_v1",
            "identified": self.identified,
            "ident_status": self.ident_status,
            "ledger_classification": self.ledger_classification,
            "record_status": self.record_status,
            "task_id": self.task_id,
            "bound_fallback_cron_id": self.bound_fallback_cron_id,
            "ownership_crosscheck": {
                "own_task_id_match": self.own_task_id_match,
                "own_chat_id_owned": self.own_chat_id_owned,
                "own_role_fallback": self.own_role_fallback,
                "own_cron_id_bound": self.own_cron_id_bound,
                "ownership_all_satisfied": self.ownership_all_satisfied,
            },
            "chat_id": self.chat_id,
            "role": self.role,
            "ledger_path": self.ledger_path,
            "canonical_root": self.canonical_root,
            "reason": self.reason,
        }


@dataclass
class CancelOnSuccessAdoptionResult:
    """task-2604 cancel-on-success live adoption 1회 실행 결과.

    ``normal_success_unchanged`` 는 디커플 절대불변 True — 채택 분류·
    cancel 실패·skip·예외 무엇이 와도 normal collector durable-success 를
    뒤집지 않는다(회장 §3 decouple).
    """

    adoption_classification: str
    identification: DurableSuccessIdentification
    durable_success: bool
    normal_success_unchanged: bool
    decouple_invariant_held: bool
    cancel_adopted: bool
    cancel_failed_classified: bool
    non_blocking_marked: bool
    lookup_status: str
    seam_invoked: bool
    seam_classification: Optional[str]
    cron_remove_invoked: bool
    fallback_cancelled: bool
    remove_attempted: bool
    remove_result: str
    fallback_preserved: bool
    five_condition_results: Optional[dict]
    target_cron_id: str
    event_id: str
    adoption_audit: dict
    adoption_audit_path: Optional[str]
    underlying_cancel_audit: dict = field(default_factory=dict)
    live_result: Optional[CancelOnSuccessLiveResult] = None
    skip_reason: str = ""
    notes: List[str] = field(default_factory=list)
    ts_utc: str = ""


def identify_normal_callback_durable_success(
    *,
    task_id: str,
    expected_chat_id: Union[int, str] = ANU_CHAT_ID,
    expected_role: str = FALLBACK_ROLE,
    ledger_path: Optional[Union[str, Path]] = None,
) -> DurableSuccessIdentification:
    """durable 4-tuple registry(+44) 에서 normal-callback durable-success
    1건 식별 + unrelated cron 제거 방지 4-tuple ownership 교차검증.

    callback_4tuple_index.jsonl 은 track 미소유 공유 durable 인프라 —
    본 함수는 **read-only query 만** 수행한다(append/rewrite 0).
    """
    croot = canonical_root()
    lp = (
        Path(ledger_path)
        if ledger_path is not None
        else default_ledger_path(croot)
    )
    try:
        registry = Callback4TupleRegistry(lp)
        verdict = registry.classify(
            task_id=task_id,
            expected_task_id=task_id,
        )
        rec = registry.latest_for(task_id)
    except Exception as exc:  # noqa: BLE001 - 디커플: 식별 예외 비전파
        return DurableSuccessIdentification(
            identified=False,
            ident_status=IDENT_NO_LEDGER_RECORD,
            ledger_classification="LEDGER_READ_ERROR",
            record_status=None,
            task_id=task_id,
            bound_fallback_cron_id=None,
            ledger_path=str(lp),
            canonical_root=str(croot),
            reason=(
                "durable registry read 예외 → durable-success 미식별, "
                f"cron-remove 0, fallback 보존(NON_BLOCKING): {exc}"
            ),
        )

    if rec is None or verdict == NO_LEDGER_RECORD:
        return DurableSuccessIdentification(
            identified=False,
            ident_status=IDENT_NO_LEDGER_RECORD,
            ledger_classification=verdict,
            record_status=None,
            task_id=task_id,
            bound_fallback_cron_id=None,
            ledger_path=str(lp),
            canonical_root=str(croot),
            reason=(
                "durable 4-tuple ledger 에 record 부재 → normal-callback "
                "durable-success 미식별, cron-remove 0, 보존(NON_BLOCKING)"
            ),
        )

    if verdict != NORMAL_CALLBACK_COMPLETED or rec.status != "COMPLETED":
        return DurableSuccessIdentification(
            identified=False,
            ident_status=(
                IDENT_TRACK_MISMATCH
                if verdict not in (NORMAL_CALLBACK_COMPLETED,)
                and rec.task_id != task_id
                else IDENT_NOT_COMPLETED
            ),
            ledger_classification=verdict,
            record_status=rec.status,
            task_id=task_id,
            bound_fallback_cron_id=rec.fallback_callback_cron_id,
            chat_id=rec.chat_id,
            role=rec.role,
            ledger_path=str(lp),
            canonical_root=str(croot),
            reason=(
                "durable record 가 normal-callback durable-success(COMPLETED)"
                f" 아님 (classify={verdict}, status={rec.status!r}) → "
                "cron-remove 0, 보존(NON_BLOCKING)"
            ),
        )

    # ── unrelated cron 제거 방지: 4-tuple ownership 교차검증 ───────────────
    own_task = rec.task_id == task_id
    own_chat = str(rec.chat_id) == str(expected_chat_id)
    own_role = (rec.role or "") == expected_role
    own_cron = bool(rec.fallback_callback_cron_id)
    all_ok = own_task and own_chat and own_role and own_cron
    return DurableSuccessIdentification(
        identified=all_ok,
        ident_status=(
            IDENT_DURABLE_SUCCESS if all_ok else IDENT_TRACK_MISMATCH
        ),
        ledger_classification=verdict,
        record_status=rec.status,
        task_id=task_id,
        bound_fallback_cron_id=rec.fallback_callback_cron_id,
        own_task_id_match=own_task,
        own_chat_id_owned=own_chat,
        own_role_fallback=own_role,
        own_cron_id_bound=own_cron,
        ownership_all_satisfied=all_ok,
        chat_id=rec.chat_id,
        role=rec.role,
        ledger_path=str(lp),
        canonical_root=str(croot),
        reason=(
            "normal-callback durable-success 1건 식별 + 4-tuple ownership "
            "일치(task_id/chat_id/role/fallback_cron_id) — bound fallback "
            "안전 cancel 진행 가능"
            if all_ok
            else (
                "durable-success 는 있으나 4-tuple ownership 불일치 "
                f"(task={own_task},chat={own_chat},role={own_role},"
                f"cron={own_cron}) → unrelated cron 제거 방지, cron-remove "
                "0, 보존(NON_BLOCKING)"
            )
        ),
    )


def _classify_adoption(
    *,
    identification: DurableSuccessIdentification,
    live: CancelOnSuccessLiveResult,
) -> tuple[str, str, Optional[str]]:
    """+45 live 결과 + 식별 결과 → task-2604 채택 분류(3종).

    반환: (adoption_classification, skip_reason, seam_classification)
    """
    if not identification.identified:
        return (
            ADOPT_NON_BLOCKING,
            identification.reason,
            None,
        )
    if live.lookup.status != LOOKUP_VERIFIED:
        return (
            ADOPT_NON_BLOCKING,
            f"durable lookup {live.lookup.status}: {live.lookup.reason}",
            None,
        )

    wired = live.wired_result
    so = (
        wired.wiring_result.seam_outcome
        if wired is not None and wired.wiring_result is not None
        else None
    )
    if so is None:
        return (
            ADOPT_NON_BLOCKING,
            "seam outcome 부재 → cron-remove 미진입, 보존(NON_BLOCKING)",
            None,
        )
    sc = so.seam_classification or ""

    # +9a 진입 (PLUS9A_<CancelClassification>)
    if sc.startswith("PLUS9A_"):
        cc = sc[len("PLUS9A_"):]
        if cc in ("CANCELLED", "ALREADY_GONE", "ALREADY_FIRED"):
            return (
                ADOPT_CANCEL_ADOPTED,
                "",
                sc,
            )
        if cc == "REMOVE_FAILED_WARNING":
            return (
                ADOPT_CANCEL_FAILED_CLASSIFIED,
                (
                    "cron remove 시도 실패 → 안전 분류 "
                    "(CANCEL_FAILED_CLASSIFIED). normal collector "
                    "durable-success 무효화 0 — fallback 은 DUPLICATE 경로 "
                    "음소거(디커플)"
                ),
                sc,
            )
        # SKIPPED_NORMAL_FAILED / SKIPPED_UNTRUSTED / ALREADY_* 기타
        return (
            ADOPT_NON_BLOCKING,
            (
                f"+9a {cc} → 추정 remove 0, fallback 보존 "
                f"(NON_BLOCKING): {so.skip_reason}"
            ),
            sc,
        )

    # live verifier 5조건 미충족 / durable gate / seam 예외 → 보존
    return (
        ADOPT_NON_BLOCKING,
        (
            f"live verifier/seam 미충족({sc}) → cron-remove 0, fallback "
            f"보존(NON_BLOCKING): {so.skip_reason}"
        ),
        sc,
    )


def run_cancel_on_success_live_adoption(
    inp: CallbackInput,
    ack_path: Union[str, Path],
    *,
    dispatch_fired_marker_path: Union[str, Path],
    result_json_path: Union[str, Path],
    report_path: Union[str, Path],
    collector_result_marker_path: Union[str, Path],
    claim_dir: Union[str, Path],
    ledger_path: Optional[Union[str, Path]] = None,
    autoset_cwd: Optional[Union[str, Path]] = None,
    adoption_audit_path: Optional[Union[str, Path]] = None,
    cancel_audit_path: Optional[Union[str, Path]] = None,
    cron_lister: Optional[Callable[[], dict]] = None,
    remover: Optional[Callable[..., object]] = None,
    fallback_cancelled_marker_path: Optional[Union[str, Path]] = None,
    cancel_lock_path: Optional[Union[str, Path]] = None,
    seam_audit_path: Optional[Union[str, Path]] = None,
    callback_contract: Optional[dict] = None,
    expected_chat_id: Union[int, str] = ANU_CHAT_ID,
    expected_role: str = FALLBACK_ROLE,
    post_result_review_fn: Callable[
        [dict, Classification], PostResultReview
    ] = no_real_codex_post_result_review,
    duplicate_callback_seen: Optional[list] = None,
    evidence_paths: Optional[list] = None,
    post_result_review_marker_path: Optional[Union[str, Path]] = None,
    ts_utc: str = "",
) -> CancelOnSuccessAdoptionResult:
    """task-2604 cancel-on-success live adoption 진입점 (회장 §3 결선).

    신규 cron/cancel 로직 0 — durable-success 식별 + ownership 교차검증
    가드를 얹고 +45 ``run_cancel_on_success_live_wiring`` (PRIMARY)
    경유한다. 어떤 seam/cron-remove 예외·skip·실패도 normal collector
    durable-success 를 변경하지 않는다(디커플 — +45/+25/+37 권위 그대로).
    """
    ts = ts_utc or _now_utc()

    # ── 1. durable-success 식별 + 4-tuple ownership 교차검증 ──────────────
    identification = identify_normal_callback_durable_success(
        task_id=inp.task_id,
        expected_chat_id=expected_chat_id,
        expected_role=expected_role,
        ledger_path=ledger_path,
    )

    # ── 2. +45 cancel-on-success live wiring 경유 (PRIMARY) ───────────────
    # 식별 실패여도 +45 는 normal completion callback collector 를 MANDATORY
    # 1회 실행(byte-0)하고 seam/cron-remove 만 skip 한다 — callback
    # mandatory rule 약화 0. lookup 은 +45 가 durable registry 에서 안전
    # 수행(동일 ledger·동일 LOOKUP_VERIFIED 게이트).
    live = run_cancel_on_success_live_wiring(
        inp,
        ack_path,
        dispatch_fired_marker_path=dispatch_fired_marker_path,
        result_json_path=result_json_path,
        report_path=report_path,
        collector_result_marker_path=collector_result_marker_path,
        claim_dir=claim_dir,
        ledger_path=ledger_path,
        autoset_cwd=autoset_cwd,
        cancel_audit_path=cancel_audit_path,
        cron_lister=cron_lister,
        remover=remover,
        fallback_cancelled_marker_path=fallback_cancelled_marker_path,
        cancel_lock_path=cancel_lock_path,
        seam_audit_path=seam_audit_path,
        callback_contract=callback_contract,
        expected_chat_id=expected_chat_id,
        expected_role=expected_role,
        post_result_review_fn=post_result_review_fn,
        duplicate_callback_seen=duplicate_callback_seen,
        evidence_paths=evidence_paths,
        post_result_review_marker_path=post_result_review_marker_path,
    )

    # ── 3. 채택 분류 (3종) ────────────────────────────────────────────────
    adoption_cls, skip_reason, seam_cls = _classify_adoption(
        identification=identification, live=live
    )

    wired = live.wired_result
    so = (
        wired.wiring_result.seam_outcome
        if wired is not None and wired.wiring_result is not None
        else None
    )
    cron_remove_invoked = bool(so.cron_remove_invoked) if so else False
    fallback_cancelled = bool(so.fallback_cancelled) if so else False
    five = (so.live_verification or {}).get("checks") if so else None
    remove_attempted = cron_remove_invoked
    if adoption_cls == ADOPT_CANCEL_ADOPTED:
        remove_result = "CANCELLED" if fallback_cancelled else "REMOVE_IDEMPOTENT"
    elif adoption_cls == ADOPT_CANCEL_FAILED_CLASSIFIED:
        remove_result = "REMOVE_FAILED_CLASSIFIED"
    else:
        remove_result = "NOT_ATTEMPTED"

    # ── 4. decouple 불변 검증 (회장 §3) ───────────────────────────────────
    # normal collector durable-success(=+45/+37/+25 권위) 는 채택 분류·
    # cancel 결과와 무관해야 한다. live.normal_success_unchanged 는 +45
    # 가 절대불변 True 로 유지. 본 모듈은 그 위에 어떤 변형도 가하지 0.
    normal_success_unchanged = bool(live.normal_success_unchanged)
    decouple_held = normal_success_unchanged is True

    # ── 5. adoption audit 합성 (schema 정합) ──────────────────────────────
    target_cron = (
        live.lookup.fallback_cron_id
        or identification.bound_fallback_cron_id
        or "<UNBOUND>"
    )
    event_id = f"{inp.task_id}|{target_cron}|{adoption_cls}|{live.event_id}"
    audit = {
        "schema": ADOPTION_AUDIT_SCHEMA,
        "event_id": event_id,
        "task_id": inp.task_id,
        "target_cron_id": target_cron,
        "adoption_classification": adoption_cls,
        "normal_callback_durable_success_identified": (
            identification.identified
        ),
        "identification": identification.to_dict(),
        "lookup_source": "durable_4tuple_registry",
        "lookup_status": live.lookup.status,
        "five_condition_results": {
            "c1_task_id_match": (five or {}).get("c1_task_id_match"),
            "c2_chat_id_owned": (five or {}).get("c2_chat_id_owned"),
            "c3_role_fallback": (five or {}).get("c3_role_fallback"),
            "c4_marker_id_crosscheck": (five or {}).get(
                "c4_marker_id_crosscheck"
            ),
            "c5_pending_not_fired_not_removed": (five or {}).get(
                "c5_pending_not_fired_not_removed"
            ),
        },
        "seam_invoked": bool(live.seam_invoked),
        "seam_classification": seam_cls,
        "remove_attempted": bool(remove_attempted),
        "remove_result": remove_result,
        "cron_remove_invoked": cron_remove_invoked,
        "fallback_cancelled": fallback_cancelled,
        "cancel_adopted": adoption_cls == ADOPT_CANCEL_ADOPTED,
        "cancel_failed_classified": (
            adoption_cls == ADOPT_CANCEL_FAILED_CLASSIFIED
        ),
        "non_blocking_marked": adoption_cls == ADOPT_NON_BLOCKING,
        "fallback_preserved": bool(live.fallback_preserved),
        "normal_success_unchanged": True,  # 디커플 절대불변 (회장 §3)
        "decouple_invariant_held": decouple_held,
        "durable_success": bool(live.durable_success),
        "wired_via_operational_collector_wiring": bool(
            live.wired_via_operational_collector_wiring
        ),
        "underlying_cancel_audit": live.cancel_audit,
        "underlying_cancel_audit_path": live.cancel_audit_path,
        "canonical_root": identification.canonical_root,
        "ledger_path": identification.ledger_path,
        "skip_reason": skip_reason,
        "notes": [
            "task-2604 Track A — read-only consume(+9a/+23/+25/+44~+55 "
            "byte-0) + +45 PRIMARY 경유, 신규 cancel 로직 0",
            "callback_4tuple_index.jsonl track 미소유 공유 durable 인프라 "
            "— 본 모듈 read-only query only(append/rewrite 0)",
            "unrelated cron 제거 방지: durable 4-tuple ownership(task_id/"
            "chat_id/role/fallback_cron_id) 일치 시에만 cancel 진행",
            "decouple: cancel 분류/실패/skip/예외 무엇이든 normal "
            "collector durable-success 무변(normal_success_unchanged=True)",
        ],
        "ts_utc": ts,
    }

    # ── 6. 다중 후보 보장 기록 (단일 디렉터리 실패 ≠ artifact 손실) ───────
    primary = (
        Path(adoption_audit_path)
        if adoption_audit_path is not None
        else Path(claim_dir) / f"{TASK_MARKER}.cancel-adoption-audit.json"
    )
    fallbacks = [
        Path(claim_dir) / f"{TASK_MARKER}.cancel-adoption-audit.json",
        Path(canonical_root())
        / "memory"
        / "events"
        / f"{TASK_MARKER}.cancel-adoption-audit.json",
    ]
    audit_written = guaranteed_write_audit(
        audit, primary=primary, fallbacks=fallbacks
    )

    return CancelOnSuccessAdoptionResult(
        adoption_classification=adoption_cls,
        identification=identification,
        durable_success=bool(live.durable_success),
        normal_success_unchanged=True,
        decouple_invariant_held=decouple_held,
        cancel_adopted=adoption_cls == ADOPT_CANCEL_ADOPTED,
        cancel_failed_classified=(
            adoption_cls == ADOPT_CANCEL_FAILED_CLASSIFIED
        ),
        non_blocking_marked=adoption_cls == ADOPT_NON_BLOCKING,
        lookup_status=live.lookup.status,
        seam_invoked=bool(live.seam_invoked),
        seam_classification=seam_cls,
        cron_remove_invoked=cron_remove_invoked,
        fallback_cancelled=fallback_cancelled,
        remove_attempted=bool(remove_attempted),
        remove_result=remove_result,
        fallback_preserved=bool(live.fallback_preserved),
        five_condition_results=five,
        target_cron_id=target_cron,
        event_id=event_id,
        adoption_audit=audit,
        adoption_audit_path=audit_written,
        underlying_cancel_audit=live.cancel_audit,
        live_result=live,
        skip_reason=skip_reason,
        notes=list(audit["notes"]),
        ts_utc=ts,
    )


__all__ = [
    "ADOPTION_AUDIT_SCHEMA",
    "ADOPT_CANCEL_ADOPTED",
    "ADOPT_CANCEL_FAILED_CLASSIFIED",
    "ADOPT_NON_BLOCKING",
    "DurableSuccessIdentification",
    "CancelOnSuccessAdoptionResult",
    "identify_normal_callback_durable_success",
    "run_cancel_on_success_live_adoption",
]
