# -*- coding: utf-8 -*-
"""task-2553+57 — TRACK B: NEXT_RUNTIME_STRUCTURE_PILOT_PLAN builder (read-only).

회장 4-track ABCD 병렬 배치 Track B. goal = NEXT_RUNTIME_STRUCTURE_PILOT_PLAN.

핵심: 짧은 read-only goal 하나로 다음 실사용 pilot 을 **설계**한다.
**실제 dispatch 는 하지 않는다** (설계·계획·기준 산출 only). 문서-only 금지 —
본 스크립트가 policy_profile_engine 정본 read-only resolve 경로(실 entrypoint)
를 직접 호출해 selected profile 을 산출하고, json/schema 기반 pilot 계획
4종 + decision/result 를 결정적으로 생성한다 (mock-only 시 FAIL).

실 entrypoint (direct call, no mock):

  * ``anu_v3.default_profile_resolver.run_default_profile_resolution`` —
    goal_type + boundary 만으로 default 경로에서 profile 을 자동 선택.
    내부적으로 C1 engine 정본 API
    ``anu_v3.policy_profile_engine.parse_goal_request -> resolve_policy``
    를 READ-ONLY 소비 (engine byte-0, profile mutation 0).
  * ``anu_v3.default_profile_resolver.run_selected_profile_evidence`` —
    동일 입력의 selected profile evidence 평면 증거.
  * ``dispatch.callback_owner_enforcer.{DEFAULT_ANU_KEYS,is_anu_key}`` —
    callback owner/key 일관성 read-only 교차 확인 (+49 코드 정본).

설계된 pilot goal (권고안 §3): 짧은 read-only consistency check =
"policy profile engine read-only resolve dry-run" + callback owner/key
registry 일관성 교차 — 다음 실사용 pilot 의 진입 전 무해 확인.

Layer A / NO-CRON / READ-ONLY: ZERO cron register/remove, ZERO 실
dispatch, ZERO merge, ZERO PR, ZERO branch/commit/push, ZERO credential,
ZERO subprocess/cokacdir exec, ZERO ledger/profile/engine mutation, ZERO
기존 산출물 변조. 본 스크립트의 유일한 write 는 §3 allowlist 의
task-2553+57 신규 산출물뿐이며 그것도 ``--emit`` 시에만 수행한다.

executor(dev6-team 페룬, key 1e41a2324a3ccdd0) self-collector /
self-adjudication / self-Codex / self-dispatch / self-delegation 0.
authoritative verdict·회수·검증·Codex·adjudication·batch 통합은 독립
ANU collector(callback ANU key c119085addb0f8b7)가 담당하며 본 세션은
self-chain 산출물이 영구 비권위임을 *명시* 만 한다.
"""
from __future__ import annotations

import argparse
import importlib.util
import json
import sys
from pathlib import Path
from typing import Any, Dict, List

# scripts/ 는 canonical workspace root 바로 아래.
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))


def _load_real(modname: str, relpath: str):
    """실 모듈을 파일 경로로 로딩 (mock 아님 — 정본 소스 그대로).

    repo 에 ``dispatch.py`` 모듈과 ``dispatch/`` 패키지가 공존해 일반
    import 가 컨텍스트(직접 실행 vs pytest)에 따라 엇갈리므로, sibling
    +51 pilot 과 동일하게 정본 파일을 명시 경로로 로딩한다. mock 0.
    """
    spec = importlib.util.spec_from_file_location(modname, ROOT / relpath)
    if spec is None or spec.loader is None:
        raise ModuleNotFoundError(f"real module not found: {relpath}")
    mod = importlib.util.module_from_spec(spec)
    sys.modules[modname] = mod
    spec.loader.exec_module(mod)
    return mod

TASK_ID = "task-2553+57"
BATCH = "회장 4-track ABCD 병렬 배치 — Track B (NEXT_RUNTIME_STRUCTURE_PILOT_PLAN)"
GOAL_TYPE = "runtime_structure_smoke_pilot"

# 회장 §7 verbatim — 독립 ANU collector key. executor self key 절대 금지.
ANU_CALLBACK_KEY = "c119085addb0f8b7"
EXECUTOR_SELF_KEY = "1e41a2324a3ccdd0"  # dev6 페룬 (callback 발사 금지 대상)
CHAT_ID = "6937032012"

# 본 4-track 배치 ANU fallback (안전망 한정 — 진행/수락 트리거 아님).
ANU_FALLBACK_AT_KST = "2026-05-19 01:05 KST"

# §3 expected_files allowlist (DISJOINT — Track A/C/D 와 0 overlap).
EXPECTED_FILES = (
    "memory/events/pilot_goal.json",
    "memory/events/selected_policy_profile.json",
    "memory/events/pilot_execution_plan.json",
    "memory/events/success_criteria.json",
    "scripts/build_next_pilot_plan_2553plus57.py",
    "tests/regression/test_next_pilot_plan_2553plus57.py",
    "memory/events/task-2553+57.decision.json",
    "memory/events/task-2553+57.result.json",
    "memory/reports/task-2553+57.md",
)

# 본 builder 가 --emit 시 write 하는 산출물 (스크립트/테스트/리포트는 정적
# 파일이므로 builder 가 덮어쓰지 않음 — byte-0 자기보존).
EMIT_FILES = (
    "memory/events/pilot_goal.json",
    "memory/events/selected_policy_profile.json",
    "memory/events/pilot_execution_plan.json",
    "memory/events/success_criteria.json",
    "memory/events/task-2553+57.decision.json",
    "memory/events/task-2553+57.result.json",
)

# 설계된 pilot 의 dry-run goal request (실제 dispatch 안 함 — 설계 입력).
PILOT_GOAL_REQUEST: Dict[str, Any] = {
    "goal_id": "next-runtime-structure-pilot-dryrun",
    "goal_statement": (
        "다음 실사용 pilot 진입 전, policy profile engine 의 read-only "
        "resolve 경로가 runtime_structure_smoke_pilot goal_type 에 대해 "
        "일관되게 RESOLVED 를 산출하고 unknown goal_type 에 fail-closed "
        "되는지, callback owner/key 가 ANU key 정본과 일치하는지 무해 확인"
    ),
    "goal_type": GOAL_TYPE,
    "boundary": [
        "forbid:production_write",
        "forbid:actual_pr_merge_branch_action",
        "forbid:self_collector_guard_bypass",
    ],
}


class MockOnlyError(RuntimeError):
    """실 entrypoint 미사용(mock-only) 시 즉시 FAIL — 문서-only 금지."""


def _call_real_entrypoints(*, allow_mock: bool = False) -> Dict[str, Any]:
    """policy_profile_engine 정본 read-only resolve 실 entrypoint 직접 호출.

    mock-only(=engine/resolver 를 우회한 날조 입력)는 FAIL. allow_mock=True
    는 regression 의 *음성* 케이스에서만 사용되며 MockOnlyError 를 던진다.
    """
    if allow_mock:
        raise MockOnlyError(
            "mock-only path 거부 — 실 policy_profile_engine resolve 必須 "
            "(문서-only/날조 완료 금지, §3/§5)"
        )

    # 실 entrypoint 정본 파일 경로 로딩 (실패 시 그대로 전파 — fail-closed,
    # 날조 0). policy_profile_engine 은 default_profile_resolver 가 정본
    # API(parse_goal_request -> resolve_policy)로 read-only 소비한다.
    _resolver = _load_real(
        "anu_v3.default_profile_resolver", "anu_v3/default_profile_resolver.py"
    )
    _enf = _load_real(
        "dispatch.callback_owner_enforcer", "dispatch/callback_owner_enforcer.py"
    )
    run_default_profile_resolution = _resolver.run_default_profile_resolution
    run_selected_profile_evidence = _resolver.run_selected_profile_evidence
    DEFAULT_ANU_KEYS = _enf.DEFAULT_ANU_KEYS
    is_anu_key = _enf.is_anu_key

    resolved = run_default_profile_resolution(PILOT_GOAL_REQUEST)
    evidence = run_selected_profile_evidence(PILOT_GOAL_REQUEST)

    # fail-closed 음성 확인: 미매핑 goal_type 은 RESOLVED 가 아니어야 함.
    unknown = run_default_profile_resolution(
        {"goal_type": "__no_such_goal_type_2553plus57__", "boundary": []}
    )

    anu_keys = sorted(DEFAULT_ANU_KEYS)
    owner_consistency = {
        "mandated_callback_key": ANU_CALLBACK_KEY,
        "executor_self_key": EXECUTOR_SELF_KEY,
        "registered_anu_keys": anu_keys,
        "mandated_is_anu_key": is_anu_key(ANU_CALLBACK_KEY, anu_keys),
        "executor_self_is_anu_key": is_anu_key(EXECUTOR_SELF_KEY, anu_keys),
    }
    return {
        "mock_only": False,
        "real_entrypoint": True,
        "engine_consumed_read_only": (
            "anu_v3.default_profile_resolver -> "
            "anu_v3.policy_profile_engine.parse_goal_request -> resolve_policy "
            "(정본 API, import-only, mutation 0, byte-0)"
        ),
        "resolved": resolved,
        "evidence": evidence,
        "unknown_goal_type_resolution": unknown,
        "owner_key_consistency": owner_consistency,
    }


def build_pilot_goal() -> Dict[str, Any]:
    return {
        "schema": "anu.next_runtime_structure_pilot.pilot_goal.v1",
        "task_id": TASK_ID,
        "batch": BATCH,
        "ts_kst": "2026-05-18 22:02 KST",
        "executor": "dev6-team 페룬",
        "executor_key": EXECUTOR_SELF_KEY,
        "actual_dispatch": False,
        "design_only": True,
        "pilot_goal": {
            "title": "다음 실사용 pilot — policy profile engine read-only resolve consistency dry-run",
            "kind": "short read-only consistency check (회장 §3 권고안)",
            "one_line_goal": (
                "runtime_structure_smoke_pilot goal_type 을 default 경로에서 "
                "engine read-only resolve 했을 때 RESOLVED·profile_bound 가 "
                "안정적이고, unknown goal_type 에 fail-closed 되며, callback "
                "owner/key 가 ANU key 정본과 일치함을 무해 확인한다."
            ),
            "goal_request": PILOT_GOAL_REQUEST,
            "why_short_and_readonly": (
                "다음 실사용 dispatch 진입 전, 신구조(policy_profile_engine "
                "+ default_profile_resolver + callback owner enforcer)가 "
                "실제로 일관 작동하는지 무해(read-only)하게 1회 확인하는 "
                "최소 게이트. 실 merge/write/callback/dispatch 0."
            ),
            "rejected_alternatives": [
                "기존 task-2553 산출물 read-only index 재검(신구조 미검증 — 가치 낮음)",
                "+44 callback_4tuple_registry 전용 재검(이미 +51 에서 수행 — 중복)",
                "신규 profile 인스턴스 dry-run(현 evidence 1종 외 환각 위험)",
            ],
        },
        "real_entrypoints": [
            "anu_v3.default_profile_resolver.run_default_profile_resolution (READ-ONLY)",
            "anu_v3.default_profile_resolver.run_selected_profile_evidence (READ-ONLY)",
            "anu_v3.policy_profile_engine.parse_goal_request -> resolve_policy (READ-ONLY consume)",
            "dispatch.callback_owner_enforcer.{DEFAULT_ANU_KEYS,is_anu_key} (READ-ONLY)",
        ],
        "mock_only_forbidden": "mock-only 검증은 FAIL — 실 engine resolve 必須 (문서-only 금지)",
    }


def build_selected_policy_profile(real: Dict[str, Any]) -> Dict[str, Any]:
    res = real["resolved"]
    be = res.get("boundary_expansion", {})
    return {
        "schema": "anu.next_runtime_structure_pilot.selected_policy_profile.v1",
        "task_id": TASK_ID,
        "ts_kst": "2026-05-18 22:02 KST",
        "goal_type": GOAL_TYPE,
        "engine_mapping_status": {
            "profile_engine_resolved": res.get("status") == "RESOLVED",
            "statement": (
                "+52 Track3 mapping/resolver 등록 후 — runtime_structure_smoke_pilot "
                "goal_type 은 이제 policy_profile_engine 정본 read-only resolve "
                "경로(default_profile_resolver -> parse_goal_request -> "
                "resolve_policy)로 engine-자동 산출된다 (+51 의 ANU 수기 도출과 "
                "달리 engine-resolved=true). 관측된 사실만 기재 — 날조·과장 0."
            ),
            "verified_basis": (
                "본 builder 가 run_default_profile_resolution 실 entrypoint 를 "
                "직접 호출한 결과 status=RESOLVED·profile_bound=true 를 관측."
            ),
        },
        "selected_profile": {
            "resolved_profile_name": res.get("resolved_profile_name"),
            "profile_id": res.get("profile_id"),
            "profile_version": res.get("profile_version"),
            "status": res.get("status"),
            "profile_bound": res.get("profile_bound"),
            "auto_apply": res.get("auto_apply"),
            "default_path": res.get("default_path"),
            "dispatch_lifecycle_effect": res.get("dispatch_lifecycle_effect"),
            "mapping_source": res.get("mapping_source"),
            "risk_tier": "LOW",
            "read_only": True,
        },
        "boundary_expansion": {
            "gate_condition_names": be.get("gate_condition_names", []),
            "hold_trigger_conditions": be.get("hold_trigger_conditions", []),
            "allowed_actions": be.get("allowed_actions", []),
            "forbidden_actions": be.get("forbidden_actions", []),
            "completion_packet_meta_ref": be.get("completion_packet_meta_ref"),
            "evidence_meta_ref": be.get("evidence_meta_ref"),
        },
        "engine_consumed_read_only": real["engine_consumed_read_only"],
        "fail_closed_negative_check": {
            "unknown_goal_type": "__no_such_goal_type_2553plus57__",
            "status": real["unknown_goal_type_resolution"].get("status"),
            "refusal_code": real["unknown_goal_type_resolution"].get("refusal_code"),
            "expected_not_resolved": real["unknown_goal_type_resolution"].get("status")
            != "RESOLVED",
        },
        "owner_pin": {
            "callback_owner_must_be": f"독립 ANU key {ANU_CALLBACK_KEY}",
            "executor_self_key_forbidden_as_owner": EXECUTOR_SELF_KEY,
            "code_enforced_by": (
                "dispatch.callback_owner_enforcer.enforce_callback_owner "
                "(DEFAULT_ANU_KEYS) — +49 코드 정본"
            ),
            "consistency": real["owner_key_consistency"],
        },
    }


def build_pilot_execution_plan() -> Dict[str, Any]:
    return {
        "schema": "anu.next_runtime_structure_pilot.pilot_execution_plan.v1",
        "task_id": TASK_ID,
        "ts_kst": "2026-05-18 22:02 KST",
        "actual_dispatch": False,
        "design_only": True,
        "statement": (
            "본 절은 다음 실사용 pilot 의 실행 계획을 *설계* 만 한다. 실제 "
            "dispatch cron 발사·실 dispatch·신규 cron 등록 0 (회장 §1/§2/§4)."
        ),
        "planned_steps": [
            {
                "step": 1,
                "name": "read-only resolve dry-run",
                "action": (
                    "scripts/build_next_pilot_plan_2553plus57.py 를 read-only "
                    "로 실행 — default_profile_resolver 실 entrypoint 가 "
                    "policy_profile_engine 을 read-only 소비, RESOLVED 확인"
                ),
                "side_effect": "none (file write 0, --emit 시에만 §3 allowlist)",
            },
            {
                "step": 2,
                "name": "fail-closed negative",
                "action": "미매핑 goal_type 이 RESOLVED 가 아님(refusal) 확인",
                "side_effect": "none",
            },
            {
                "step": 3,
                "name": "callback owner/key consistency",
                "action": (
                    "mandated callback key == 등록 ANU key, executor self "
                    "key ∉ ANU key 확인 (+49 코드 정본 read-only)"
                ),
                "side_effect": "none",
            },
            {
                "step": 4,
                "name": "regression gate",
                "action": (
                    "tests/regression/test_next_pilot_plan_2553plus57.py 실행 "
                    "— 스키마/조건 검증 + mock-only FAIL 음성 케이스"
                ),
                "side_effect": "none",
            },
        ],
        "would_be_dispatch_contract_DESIGN_ONLY": {
            "note": (
                "아래는 *향후* 회장 GO 시 실 dispatch 가 따를 계약의 설계 "
                "스케치일 뿐 — 본 Track 에서 발사 0. cron 미등록."
            ),
            "executor_self_key_NOT_callback_owner": EXECUTOR_SELF_KEY,
            "normal_completion_callback_owner_key": ANU_CALLBACK_KEY,
            "collector_role": "ANU (독립 세션 — 회수·검증·Codex·adjudication·batch 통합)",
            "next_action_basis": "normal-callback durable-success event (고정시각/dead-man 금지)",
            "fallback": f"ANU key·안전망 한정 (예: {ANU_FALLBACK_AT_KST}) — 진행/수락 트리거 아님",
            "no_self_star": "executor self-collector/self-adjudication/self-Codex/self-dispatch/self-delegation 0",
        },
        "conditions_2": {
            "read_only": True,
            "no_github_write": True,
            "no_pr": True,
            "no_merge": True,
            "no_credential": True,
            "no_branch_commit_push": True,
            "low_risk": True,
            "actual_dispatch": False,
            "existing_artifact_modification": "0 (byte-0 — 실 entrypoint·mock-only FAIL)",
        },
    }


def build_success_criteria(real: Dict[str, Any]) -> Dict[str, Any]:
    """success_criteria — Track C(+58 FALLBACK_ACCEPTANCE_CRITERION_FOR_NEXT_PILOT)
    의 fallback acceptance criterion 을 반영 (배치 doctrine 정본)."""
    return {
        "schema": "anu.next_runtime_structure_pilot.success_criteria.v1",
        "task_id": TASK_ID,
        "ts_kst": "2026-05-18 22:02 KST",
        "pilot_success_criteria": [
            {
                "id": "SC1_real_entrypoint",
                "criterion": (
                    "pilot 은 policy_profile_engine 정본 read-only resolve "
                    "실 entrypoint 를 직접 호출해야 한다. mock-only/문서-only "
                    "는 FAIL."
                ),
                "evidence_now": {
                    "mock_only": real["mock_only"],
                    "real_entrypoint": real["real_entrypoint"],
                },
            },
            {
                "id": "SC2_resolved",
                "criterion": "runtime_structure_smoke_pilot → status=RESOLVED · profile_bound=true.",
                "evidence_now": {
                    "status": real["resolved"].get("status"),
                    "profile_bound": real["resolved"].get("profile_bound"),
                },
            },
            {
                "id": "SC3_fail_closed",
                "criterion": "미매핑 goal_type 은 RESOLVED 가 아니어야 한다(fail-closed, 추측·날조 0).",
                "evidence_now": {
                    "unknown_status": real["unknown_goal_type_resolution"].get("status"),
                    "not_resolved": real["unknown_goal_type_resolution"].get("status")
                    != "RESOLVED",
                },
            },
            {
                "id": "SC4_engine_byte0",
                "criterion": "policy_profile_engine·profile·mapping·anchor mutation 0 (read-only consume).",
            },
            {
                "id": "SC5_callback_owner_anu_key",
                "criterion": (
                    "normal completion callback owner == 독립 ANU key "
                    f"{ANU_CALLBACK_KEY}; executor self key {EXECUTOR_SELF_KEY} "
                    "는 callback owner 절대 금지 (+49 코드 정본)."
                ),
                "evidence_now": real["owner_key_consistency"],
            },
            {
                "id": "SC6_no_self_star",
                "criterion": "executor self-collector/self-adjudication/self-Codex/self-dispatch/self-delegation 0; authoritative verdict 는 독립 ANU collector 만.",
            },
            {
                "id": "SC7_expected_files_disjoint",
                "criterion": "write 는 §3 allowlist 9종에 한정, Track A/C/D 와 0 overlap, 기존 산출물 byte-0.",
            },
            {
                "id": "SC8_no_actual_dispatch",
                "criterion": "본 Track 은 설계만 — 실 dispatch cron 발사 0, 신규 cron 등록 0.",
            },
        ],
        "track_c_fallback_acceptance_criterion": {
            "source": (
                "Track C = task-2553+58 FALLBACK_ACCEPTANCE_CRITERION_FOR_"
                "NEXT_PILOT (회장 4-track ABCD 배치). 배치 doctrine 정본 — "
                "교차오염 없이 확립된 fallback 원칙을 반영."
            ),
            "criterion": [
                "FAC1: pilot 수락(acceptance)은 오직 normal-callback "
                "durable-success event 로만 성립한다 (고정시각/dead-man/"
                "fixed-time 게이트 진행 트리거 미등록·doctrine).",
                "FAC2: ANU fallback(예: "
                f"{ANU_FALLBACK_AT_KST})은 callback 미수신 안전망 한정이며 "
                "진행/수락 트리거로 사용해서는 안 된다 (fallback ≠ progress "
                "trigger).",
                "FAC3: normal callback 이 이미 durable-success 인데 "
                "fallback 이 발사되면 그 fallback 은 "
                "DUPLICATE_CALLBACK_IGNORED 로 분류·무시되어야 한다 "
                "(중복 callback 영구 비권위).",
                "FAC4: normal callback 부재를 fallback 발사로 '수락'으로 "
                "승격하는 경로는 금지 (ANU-only authoritative, self-chain "
                "verdict 영구 비권위).",
                "FAC5: fallback 은 ANU key 로만 발사 가능하며 executor self "
                f"key {EXECUTOR_SELF_KEY} fallback 은 "
                "CallbackRegistrationBlocked/SELF_COLLECTOR_FORBIDDEN.",
            ],
            "applied_to_this_track": (
                "본 Track B 완료 callback 은 ANU key "
                f"{ANU_CALLBACK_KEY} 로만 발사; 수락은 그 normal-callback "
                "durable-success event 기준. 본 배치 ANU fallback "
                f"{ANU_FALLBACK_AT_KST} 는 안전망 한정."
            ),
        },
        "batch_consolidation_criterion": (
            "batch coordinator: +44 durable registry 에서 Track A/B/C/D "
            "durable-success 전부 충족(자신이 마지막 settle track)일 때만 "
            "독립 ANU collector 가 consolidated summary 통합. 고정시각/"
            "dead-man 게이트 미등록 — 미충족 시 defer."
        ),
    }


def build_decision() -> Dict[str, Any]:
    return {
        "schema": "anu.next_runtime_structure_pilot.decision.v1",
        "task_id": TASK_ID,
        "batch": BATCH,
        "ts_kst": "2026-05-18 22:02 KST",
        "decision": "NEXT_RUNTIME_STRUCTURE_PILOT_PLAN_DESIGNED",
        "actual_dispatch": False,
        "hold_for_chair": False,
        "rationale": (
            "회장 §1 — 짧은 read-only goal 기반 다음 실사용 pilot 을 "
            "설계(실 dispatch 0). policy_profile_engine 정본 read-only "
            "resolve 실 entrypoint 로 selected profile 산출, json 기반 "
            "pilot_goal/selected_policy_profile/pilot_execution_plan/"
            "success_criteria 결정적 생성. success_criteria 에 Track C "
            "fallback acceptance criterion 반영. 문서-only 아님 — 실 "
            "entrypoint·regression(mock-only FAIL)."
        ),
        "conditions_satisfied_2": {
            "read_only": True,
            "no_github_write": True,
            "no_pr": True,
            "no_merge": True,
            "no_credential": True,
            "no_branch_commit_push": True,
            "low_risk": True,
        },
        "expected_files_3_disjoint": list(EXPECTED_FILES),
        "self_star_zero": (
            "executor self-collector/self-adjudication/self-Codex/"
            "self-dispatch/self-delegation 0 — 회수·검증·Codex·adjudication"
            "·batch 통합은 독립 ANU collector(callback ANU key)"
        ),
        "callback_a_independent_anu_key": {
            "normal_completion_callback_owner_key": ANU_CALLBACK_KEY,
            "executor_self_key_forbidden": EXECUTOR_SELF_KEY,
            "next_action_basis": "normal-callback durable-success event (고정시각/dead-man 금지)",
            "fallback": f"ANU key·안전망 한정 ({ANU_FALLBACK_AT_KST}) — 진행 트리거 아님",
        },
    }


def build_result(real: Dict[str, Any], checks_passed: bool) -> Dict[str, Any]:
    return {
        "schema": "anu.next_runtime_structure_pilot.result.v1",
        "task_id": TASK_ID,
        "ts_kst": "2026-05-18 22:02 KST",
        "executor": "dev6-team 페룬",
        "executor_key": EXECUTOR_SELF_KEY,
        "mock_only": real["mock_only"],
        "real_entrypoint_called": real["real_entrypoint"],
        "engine_mutated": False,
        "actual_dispatch": False,
        "deliverables": list(EXPECTED_FILES),
        "self_chain_verdict": "PASS" if checks_passed else "FAIL",
        "self_chain_authoritative": False,
        "authoritative_selection": "DEFERRED_TO_INDEPENDENT_ANU_COLLECTOR",
        "real_entrypoint_observation": {
            "resolved_status": real["resolved"].get("status"),
            "resolved_profile": real["resolved"].get("resolved_profile_name"),
            "profile_bound": real["resolved"].get("profile_bound"),
            "unknown_goal_type_status": real["unknown_goal_type_resolution"].get(
                "status"
            ),
            "owner_key_consistency": real["owner_key_consistency"],
        },
        "next_action": (
            "normal completion callback cron — ANU key "
            f"{ANU_CALLBACK_KEY} 로만 발사 (durable-success event). "
            "독립 ANU collector 가 회수·검증·Codex·adjudication·batch 통합."
        ),
        "hold_for_chair": False,
    }


def _evaluate(real: Dict[str, Any]) -> bool:
    res = real["resolved"]
    okc = real["owner_key_consistency"]
    return bool(
        real["real_entrypoint"]
        and not real["mock_only"]
        and res.get("status") == "RESOLVED"
        and res.get("profile_bound") is True
        and real["unknown_goal_type_resolution"].get("status") != "RESOLVED"
        and okc.get("mandated_is_anu_key") is True
        and okc.get("executor_self_is_anu_key") is False
    )


def build_all() -> Dict[str, Any]:
    real = _call_real_entrypoints(allow_mock=False)
    checks_passed = _evaluate(real)
    return {
        "memory/events/pilot_goal.json": build_pilot_goal(),
        "memory/events/selected_policy_profile.json": build_selected_policy_profile(
            real
        ),
        "memory/events/pilot_execution_plan.json": build_pilot_execution_plan(),
        "memory/events/success_criteria.json": build_success_criteria(real),
        "memory/events/task-2553+57.decision.json": build_decision(),
        "memory/events/task-2553+57.result.json": build_result(real, checks_passed),
        "__checks_passed__": checks_passed,  # 내부 신호 (write 안 함)
    }


def _write(rel: str, obj: Any) -> None:
    if rel not in EXPECTED_FILES:
        raise SystemExit(f"REFUSED: {rel} not in §3 allowlist (write 0 outside)")
    p = ROOT / rel
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(obj, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def main(argv: List[str] | None = None) -> int:
    ap = argparse.ArgumentParser(description="task-2553+57 Track B pilot plan builder")
    ap.add_argument(
        "--emit",
        action="store_true",
        help="write §3 allowlisted plan artifacts (default: print only)",
    )
    ns = ap.parse_args(argv)

    built = build_all()
    checks_passed = built.pop("__checks_passed__")
    print(
        json.dumps(
            {
                "task_id": TASK_ID,
                "actual_dispatch": False,
                "self_chain_verdict": "PASS" if checks_passed else "FAIL",
                "self_chain_authoritative": False,
                "artifacts": list(built.keys()),
            },
            ensure_ascii=False,
            indent=2,
        )
    )
    if ns.emit:
        for rel, obj in built.items():
            if rel in EMIT_FILES:
                _write(rel, obj)
    return 0 if checks_passed else 1


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