#!/usr/bin/env python3
"""Run the no-send Leo proposal lifecycle against an existing disposable clone.

The model receives one staging-only bridge verb. Review and apply remain
harness-owned phases that invoke the existing ``kb_review`` and ``kb_apply``
CLIs. The command never creates or drops the clone, restarts a service, exposes
a delivery adapter, or targets the production container for writes.
"""

from __future__ import annotations

import argparse
import asyncio
import hashlib
import json
import re
import shlex
import subprocess
import sys
import traceback
import uuid
from collections.abc import Iterator
from contextlib import contextmanager
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace
from typing import Any

HERE = Path(__file__).resolve().parent
REPO_ROOT = HERE.parent
sys.path.insert(0, str(HERE))

import apply_proposal as ap  # noqa: E402
import run_approve_claim_clone_canary as guarded  # noqa: E402
import run_leo_clone_bound_handler_checkpoint as bound  # noqa: E402

SCHEMA = "livingip.leoCloneLifecycleCheckpoint.v1"
STAGE_COMMAND = "stage-lifecycle-claim"
INTERNAL_STAGE_COMMAND = "_stage-lifecycle-claim"
DEFAULT_REVIEW_SECRETS_FILE = guarded.DEFAULT_REVIEW_SECRETS_FILE
DEFAULT_APPLY_SECRETS_FILE = ap.DEFAULT_SECRETS_FILE
DEFAULT_REVIEW_ROLE = "kb_review"
DEFAULT_APPLY_ROLE = ap.DEFAULT_ROLE
REVIEWER_HANDLE = guarded.REVIEWER_HANDLE
REVIEW_NOTE = guarded.REVIEW_NOTE
APPLIED_BY_HANDLE = "kb-apply"

MARKER_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:-]{7,127}\Z")
APPROVAL_TABLE = "kb_stage.kb_proposal_approvals"
LIFECYCLE_TABLES = (*bound.COUNT_TABLES, APPROVAL_TABLE)
PRODUCTION_TABLES = bound.COUNT_TABLES
READ_ONLY_COMMANDS = frozenset(bound.READ_ONLY_BRIDGE_COMMANDS)
STAGING_COMMANDS = frozenset({STAGE_COMMAND})
LIFECYCLE_STATE_PREFIX = "LIFECYCLE_STATE:"


def new_marker(prefix: str) -> str:
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    return f"{prefix}-{timestamp}-{uuid.uuid4().hex[:12]}"


def _stable_uuid(run_marker: str, label: str) -> str:
    return str(uuid.uuid5(uuid.NAMESPACE_URL, f"livingip:leo-clone-lifecycle:{run_marker}:{label}"))


def _json_hash(value: Any) -> str:
    return hashlib.sha256(
        json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()
    ).hexdigest()


def build_lifecycle_fixture(run_marker: str) -> dict[str, Any]:
    """Build one deterministic, narrow, strict approve_claim proposal."""
    proposal_id = _stable_uuid(run_marker, "proposal")
    claim_id = _stable_uuid(run_marker, "claim")
    source_id = _stable_uuid(run_marker, "source")
    claim_text = (
        f"Lifecycle checkpoint {run_marker}: a staged knowledge change remains non-canonical "
        "until an explicit operator review and guarded apply both succeed."
    )
    source_excerpt = (
        f"No-send disposable-clone lifecycle observation for {run_marker}; the harness, not Leo, owns review and apply."
    )
    source_hash = hashlib.sha256(f"livingip-leo-clone-lifecycle\n{run_marker}\n{source_excerpt}".encode()).hexdigest()
    apply_payload = {
        "contract_version": 2,
        "source_proposal_id": None,
        "agent_id": None,
        "claims": [
            {
                "id": claim_id,
                "type": "structural",
                "text": claim_text,
                "status": "open",
                "confidence": 1.0,
                "tags": ["leo-lifecycle-checkpoint", run_marker],
                "created_by": None,
                "superseded_by": None,
            }
        ],
        "sources": [
            {
                "id": source_id,
                "source_type": "observation",
                "url": None,
                "storage_path": None,
                "excerpt": source_excerpt,
                "hash": source_hash,
                "created_by": None,
            }
        ],
        "evidence": [
            {
                "claim_id": claim_id,
                "source_id": source_id,
                "role": "grounds",
                "weight": 1.0,
                "created_by": None,
            }
        ],
        "edges": [],
        "reasoning_tools": [],
        "normalization_manifest": {
            "generator": Path(__file__).name,
            "run_marker": run_marker,
            "scope": "one_claim_one_source_one_evidence",
        },
    }
    proposal_payload = {
        "apply_payload": apply_payload,
        "lifecycle_checkpoint": {
            "run_marker": run_marker,
            "model_authority": "stage_only",
            "operator_authority": ["review", "guarded_apply"],
        },
    }
    return {
        "run_marker": run_marker,
        "proposal_id": proposal_id,
        "claim_id": claim_id,
        "source_id": source_id,
        "source_ref": f"leo-clone-lifecycle:{run_marker}",
        "rationale": ("Stage one narrow lifecycle claim for explicit operator review in a disposable clone."),
        "apply_payload": apply_payload,
        "proposal_payload": proposal_payload,
    }


def _proposal_json_sql(proposal_id: str) -> str:
    return f"""select jsonb_build_object(
  'id', id::text,
  'proposal_type', proposal_type,
  'status', status,
  'channel', channel,
  'source_ref', source_ref,
  'rationale', rationale,
  'payload', payload,
  'reviewed_by_handle', reviewed_by_handle,
  'reviewed_by_agent_id', reviewed_by_agent_id::text,
  'reviewed_at', reviewed_at::text,
  'review_note', review_note,
  'applied_by_handle', applied_by_handle,
  'applied_by_agent_id', applied_by_agent_id::text,
  'applied_at', applied_at::text
) from kb_stage.kb_proposals where id = {ap.sql_literal(proposal_id)}::uuid"""


def build_stage_sql(fixture: dict[str, Any]) -> str:
    """Build an idempotent staging-only statement; it never touches public.*."""
    proposal_id = ap.sql_literal(fixture["proposal_id"])
    source_ref = ap.sql_literal(fixture["source_ref"])
    rationale = ap.sql_literal(fixture["rationale"])
    payload = ap.sql_literal(json.dumps(fixture["proposal_payload"], sort_keys=True))
    proposal_select = _proposal_json_sql(fixture["proposal_id"])
    return f"""\\set ON_ERROR_STOP on
begin;
create temporary table lifecycle_stage_result (created boolean not null) on commit drop;
with inserted as (
  insert into kb_stage.kb_proposals
    (id, proposal_type, status, channel, source_ref, rationale, payload)
  values
    ({proposal_id}::uuid, 'approve_claim', 'pending_review', 'clone_lifecycle',
     {source_ref}, {rationale}, {payload}::jsonb)
  on conflict (id) do nothing
  returning id
)
insert into lifecycle_stage_result(created)
select exists(select 1 from inserted);

do $lifecycle$
declare
  marker_count integer;
  exact_count integer;
begin
  select count(*) into marker_count
    from kb_stage.kb_proposals
   where source_ref = {source_ref};
  select count(*) into exact_count
    from kb_stage.kb_proposals
   where id = {proposal_id}::uuid
     and proposal_type = 'approve_claim'
     and status = 'pending_review'
     and channel = 'clone_lifecycle'
     and source_ref = {source_ref}
     and rationale = {rationale}
     and payload = {payload}::jsonb
     and reviewed_by_handle is null
     and reviewed_by_agent_id is null
     and reviewed_at is null
     and review_note is null
     and applied_by_handle is null
     and applied_by_agent_id is null
     and applied_at is null;
  if marker_count <> 1 or exact_count <> 1 then
    raise exception 'lifecycle stage validation failed: marker_count=%, exact_count=%',
      marker_count, exact_count;
  end if;
end
$lifecycle$;

select jsonb_build_object(
  'created', (select created from lifecycle_stage_result),
  'marker_match_count', (
    select count(*) from kb_stage.kb_proposals where source_ref = {source_ref}
  ),
  'proposal', ({proposal_select})
)::text;
commit;
"""


def target_unrelated_guard_snapshot(container: str, database: str, fixture: dict[str, Any]) -> dict[str, Any]:
    """Fingerprint all lifecycle-table rows except this run's deterministic rows."""
    predicates = {
        "public.claims": f"t.id <> {ap.sql_literal(fixture['claim_id'])}::uuid",
        "public.sources": f"t.id <> {ap.sql_literal(fixture['source_id'])}::uuid",
        "public.claim_evidence": (
            "not ("
            f"t.claim_id = {ap.sql_literal(fixture['claim_id'])}::uuid and "
            f"t.source_id = {ap.sql_literal(fixture['source_id'])}::uuid and "
            "t.role::text = 'grounds')"
        ),
        "public.claim_edges": "true",
        "public.reasoning_tools": "true",
        "kb_stage.kb_proposals": f"t.id <> {ap.sql_literal(fixture['proposal_id'])}::uuid",
        APPROVAL_TABLE: f"t.proposal_id <> {ap.sql_literal(fixture['proposal_id'])}::uuid",
    }
    pairs = []
    for table in LIFECYCLE_TABLES:
        predicate = predicates[table]
        pairs.append(
            f"""'{table}', jsonb_build_object(
    'count', (select count(*) from {table} t where {predicate}),
    'rowset_md5', (select md5(coalesce(string_agg(to_jsonb(t)::text, E'\\n'
      order by to_jsonb(t)::text), '')) from {table} t where {predicate})
  )"""
        )
    return bound._psql_json(
        container,
        database,
        """\\set ON_ERROR_STOP on
begin transaction read only;
select jsonb_build_object(
  """
        + ",\n  ".join(pairs)
        + """
)::text;
rollback;
""",
    )


def _validate_marker(marker: str, flag: str) -> None:
    if not MARKER_RE.fullmatch(marker):
        raise bound.CheckpointError(
            f"{flag} must be 8-128 characters using only letters, digits, '.', '_', ':', or '-'"
        )


def assert_disposable_target(container: str, database: str) -> tuple[dict[str, Any], dict[str, Any]]:
    if not bound.SAFE_DOCKER_NAME_RE.fullmatch(container):
        raise bound.CheckpointError("--container must be an explicit Docker name")
    if container == bound.PRODUCTION_CONTAINER:
        raise bound.CheckpointError("--container must not target the production teleo-pg container")
    if not bound.SAFE_DB_NAME_RE.fullmatch(database):
        raise bound.CheckpointError("--db must be an explicit Postgres database name")
    target = bound.container_identity(container)
    production = bound.container_identity(bound.PRODUCTION_CONTAINER)
    if target["id"] == production["id"]:
        raise bound.CheckpointError("supplied target resolves to the production container identity")
    bound.validate_disposable_target_identity(target, production)
    return target, production


def stage_lifecycle_proposal(container: str, database: str, fixture: dict[str, Any]) -> dict[str, Any]:
    """Stage or rediscover the exact deterministic proposal in the clone."""
    assert_disposable_target(container, database)
    result = bound._psql_json(container, database, build_stage_sql(fixture))
    proposal = result.get("proposal") if isinstance(result, dict) else None
    if not proposal:
        raise bound.CheckpointError("staging command returned no proposal")
    expected = {
        "id": fixture["proposal_id"],
        "proposal_type": "approve_claim",
        "status": "pending_review",
        "channel": "clone_lifecycle",
        "source_ref": fixture["source_ref"],
        "rationale": fixture["rationale"],
        "payload": fixture["proposal_payload"],
    }
    mismatches = {
        key: {"expected": value, "actual": proposal.get(key)}
        for key, value in expected.items()
        if proposal.get(key) != value
    }
    if mismatches:
        raise bound.CheckpointError(
            "deterministic lifecycle proposal already exists with different state or content: "
            + json.dumps(mismatches, sort_keys=True)
        )
    if result.get("marker_match_count") != 1:
        raise bound.CheckpointError("run marker did not resolve to exactly one staged proposal")
    return {
        "created": result.get("created") is True,
        "idempotent_existing": result.get("created") is False,
        "marker_match_count": result.get("marker_match_count"),
        "proposal": proposal,
        "canonical_row_ids": {
            "public.claims": [fixture["claim_id"]],
            "public.sources": [fixture["source_id"]],
            "public.claim_evidence": [
                {
                    "claim_id": fixture["claim_id"],
                    "source_id": fixture["source_id"],
                    "role": "grounds",
                }
            ],
        },
    }


def build_lifecycle_wrapper(
    *,
    kb_tool: Path,
    tool_log: Path,
    container: str,
    database: str,
    run_marker: str,
    run_nonce: str,
) -> str:
    script = Path(__file__).resolve()
    python = Path(sys.executable).resolve()
    return f"""#!/bin/bash
set -uo pipefail

KB_TOOL={shlex.quote(str(kb_tool))}
TOOL_LOG={shlex.quote(str(tool_log))}
TARGET_CONTAINER={shlex.quote(container)}
TARGET_DATABASE={shlex.quote(database)}
LIFECYCLE_SCRIPT={shlex.quote(str(script))}
PYTHON={shlex.quote(str(python))}
DOCKER={shlex.quote(bound.SYSTEM_DOCKER)}
export PATH={shlex.quote(bound.SYSTEM_EXEC_PATH)}
RUN_MARKER={shlex.quote(run_marker)}
RUN_NONCE={shlex.quote(run_nonce)}
INVOCATION_ID="$($PYTHON -c 'import uuid; print(uuid.uuid4())')"
DB_RECEIPT="$($DOCKER exec "$TARGET_CONTAINER" psql -U postgres -d "$TARGET_DATABASE" -At -q -v ON_ERROR_STOP=1 -c "select jsonb_build_object('current_database', current_database(), 'system_identifier', (select system_identifier::text from pg_control_system()), 'transaction_read_only', current_setting('transaction_read_only'))::text")"
if [[ -z "$DB_RECEIPT" ]]; then
  exit 125
fi

$PYTHON - "$TOOL_LOG" "$RUN_NONCE" "$INVOCATION_ID" "$TARGET_CONTAINER" "$TARGET_DATABASE" "$DB_RECEIPT" "$@" <<'PY'
import json
import sys
from datetime import datetime, timezone

path, run_nonce, invocation_id, container, database, db_receipt, *argv = sys.argv[1:]
with open(path, "a", encoding="utf-8") as handle:
    handle.write(json.dumps({{
        "phase": "start",
        "at_utc": datetime.now(timezone.utc).isoformat(),
        "invocation_id": invocation_id,
        "run_nonce": run_nonce,
        "container": container,
        "database": database,
        "database_identity": json.loads(db_receipt),
        "argv": argv,
    }}, sort_keys=True) + "\\n")
PY

if [[ "${{1:-}}" == {shlex.quote(STAGE_COMMAND)} ]]; then
  if [[ "$#" -ne 1 ]]; then
    printf '%s\n' '{STAGE_COMMAND} accepts no model-supplied arguments' >&2
    STATUS=64
  else
    "$PYTHON" "$LIFECYCLE_SCRIPT" {shlex.quote(INTERNAL_STAGE_COMMAND)} \
      --container "$TARGET_CONTAINER" --db "$TARGET_DATABASE" --run-marker "$RUN_MARKER"
    STATUS=$?
  fi
else
  "$PYTHON" "$KB_TOOL" --local --container "$TARGET_CONTAINER" --db "$TARGET_DATABASE" "$@"
  STATUS=$?
fi

$PYTHON - "$TOOL_LOG" "$RUN_NONCE" "$INVOCATION_ID" "$TARGET_CONTAINER" "$TARGET_DATABASE" "$STATUS" <<'PY'
import json
import sys
from datetime import datetime, timezone

path, run_nonce, invocation_id, container, database, status = sys.argv[1:]
with open(path, "a", encoding="utf-8") as handle:
    handle.write(json.dumps({{
        "phase": "end",
        "at_utc": datetime.now(timezone.utc).isoformat(),
        "invocation_id": invocation_id,
        "run_nonce": run_nonce,
        "container": container,
        "database": database,
        "returncode": int(status),
    }}, sort_keys=True) + "\\n")
PY

exit "$STATUS"
"""


def patch_lifecycle_bridge(
    temp_profile: Path,
    *,
    container: str,
    database: str,
    fixture: dict[str, Any],
) -> dict[str, Any]:
    bridge = bound.patch_temp_bridge(temp_profile, container, database)
    wrapper = Path(bridge["wrapper_path"])
    kb_tool = temp_profile / "bin" / "kb_tool.py"
    tool_log = Path(bridge["tool_log_path"])
    skill_path = Path(bridge["bridge_skill_path"])
    wrapper_text = build_lifecycle_wrapper(
        kb_tool=kb_tool,
        tool_log=tool_log,
        container=container,
        database=database,
        run_marker=fixture["run_marker"],
        run_nonce=bridge["run_nonce"],
    )
    skill_text = skill_path.read_text(encoding="utf-8")
    skill_text = skill_text.replace(
        """End the final answer with one machine-readable line using the full proposal
UUID and the row state you observed:
`KB_STATE: {\"proposal_id\":\"<uuid>\",\"status\":\"pending_review|approved|applied\",\"applied\":false}`

""",
        "",
    )
    skill_text = skill_text.replace(
        "This checkpoint exposes\nonly read-only bridge verbs and records every invocation in its receipt.",
        "This checkpoint exposes read-only bridge verbs plus the single staging-only verb documented above, "
        "and records every invocation in its receipt.",
    )
    lifecycle_binding = f"""
## Disposable Lifecycle Staging Authority

This no-send profile may make exactly one kind of write, only in the supplied
disposable clone: `{wrapper} {STAGE_COMMAND}`. The command accepts no arguments
and stages one deterministic `approve_claim` row as `pending_review` for run
marker `{fixture["run_marker"]}`. Use it when the operator asks for the narrowest
reviewable change, then read the returned proposal with `show-proposal`.

You cannot approve or apply proposals. Do not claim that a staged or approved
proposal is canonical. Only the external harness may invoke the separate
`kb_review` and `kb_apply` paths after your turn has ended.

Each prompt defines one exact `LIFECYCLE_STATE:` JSON receipt. Put that receipt
on the final line of the answer, using full UUIDs and values actually observed
from the clone. Never substitute the generic `KB_STATE:` receipt in this
lifecycle checkpoint.
"""
    skill_text = bound.inject_skill_binding(skill_text, lifecycle_binding.lstrip())
    bound._detach_and_write(wrapper, wrapper_text, mode=0o700)
    bound._detach_and_write(skill_path, skill_text, mode=0o600)
    bridge.update(
        {
            "wrapper_sha256": bound.sha256_bytes(wrapper_text.encode()),
            "bridge_skill_sha256": bound.sha256_bytes(skill_text.encode()),
            "staging_command": STAGE_COMMAND,
            "staging_command_accepts_model_arguments": False,
            "staging_target_is_hard_bound": True,
            "model_can_approve": False,
            "model_can_apply": False,
        }
    )
    return bridge


@contextmanager
def lifecycle_gateway_surface(*, allow_staging: bool) -> Iterator[None]:
    """Expose the staging verb only for the one explicitly authorized turn."""
    original_commands = bound.READ_ONLY_BRIDGE_COMMANDS
    original_install = bound.install_checkpoint_tool_surface
    original_validator = bound.validate_read_only_bridge_argv

    def install(temp_profile: Path) -> dict[str, Any]:
        surface = original_install(temp_profile)
        surface.update(
            {
                "read_only_bridge_commands": sorted(READ_ONLY_COMMANDS),
                "staging_bridge_commands": sorted(STAGING_COMMANDS) if allow_staging else [],
                "model_can_stage_pending_proposal": allow_staging,
                "model_can_approve_or_apply": False,
                "operator_review_apply_tools_exposed": False,
            }
        )
        return surface

    def validate(argv: list[str]) -> None:
        if allow_staging and argv == [STAGE_COMMAND]:
            return
        original_validator(argv)

    bound.READ_ONLY_BRIDGE_COMMANDS = frozenset(
        READ_ONLY_COMMANDS | STAGING_COMMANDS if allow_staging else READ_ONLY_COMMANDS
    )
    bound.install_checkpoint_tool_surface = install
    bound.validate_read_only_bridge_argv = validate
    try:
        yield
    finally:
        bound.READ_ONLY_BRIDGE_COMMANDS = original_commands
        bound.install_checkpoint_tool_surface = original_install
        bound.validate_read_only_bridge_argv = original_validator


def lifecycle_prompts(fixture: dict[str, Any], conversation_marker: str) -> dict[str, str]:
    proposal_id = fixture["proposal_id"]
    claim_id = fixture["claim_id"]
    source_id = fixture["source_id"]
    return {
        "T1_conversation_marker": (
            "Remember this exact conversation-only marker for a later turn: "
            f"{conversation_marker}. Reply with the exact marker and say that remembering it does not change "
            "the canonical Postgres KB. Do not stage anything yet. End with exactly: "
            f'{LIFECYCLE_STATE_PREFIX} {{"phase":"T1","conversation_marker":"{conversation_marker}",'
            '"kb_mutated":false}'
        ),
        "T2_stage_pending_proposal": (
            "m3taversal-style request: get this into Leo now - every staged knowledge change must remain non-canonical "
            "until an operator reviews it and a guarded apply succeeds. Make the narrowest reviewable claim change, "
            f"tag this run with {fixture['run_marker']}, do not approve or apply it, and report the proposal ID and "
            f'state. End with exactly: {LIFECYCLE_STATE_PREFIX} {{"phase":"T2","proposal_id":"{proposal_id}",'
            '"status":"pending_review","canonical":false}'
        ),
        "T3_approved_unapplied_readback": (
            f"Read proposal {proposal_id} from the clone now. Tell me its exact status, whether applied_at is NULL, "
            "and whether the planned claim is already canonical. Do not stage another proposal and do not apply it. "
            f'End with exactly: {LIFECYCLE_STATE_PREFIX} {{"phase":"T3","proposal_id":"{proposal_id}",'
            '"status":"approved","applied":false,"canonical":false}'
        ),
        "T5_recall_and_applied_readback": (
            "The isolated handler has been reopened after the harness completed guarded apply. Without asking me to "
            f"repeat the conversation-only marker, recall it exactly. Then show proposal {proposal_id}, show canonical "
            f"claim {claim_id}, and report the exact proposal, claim, and source row IDs (source {source_id}) plus the "
            f'proposal status. End with exactly: {LIFECYCLE_STATE_PREFIX} {{"phase":"T5",'
            f'"conversation_marker":"<recalled exact marker>","proposal_id":"{proposal_id}",'
            f'"status":"applied","claim_id":"{claim_id}","source_id":"{source_id}"}}'
        ),
    }


def _turn_args(args: argparse.Namespace, phase: str, prompt: str) -> argparse.Namespace:
    return SimpleNamespace(
        chat_id=args.chat_id,
        user_id=args.user_id,
        user_name=args.user_name,
        prompt=prompt,
        prompt_id=phase,
        run_id=args.run_marker,
        turn_timeout=args.turn_timeout,
    )


async def run_gateway_turn(
    args: argparse.Namespace,
    temp_profile: Path,
    *,
    phase: str,
    prompt: str,
    provider_environment: dict[str, str],
    tool_log: Path,
) -> dict[str, Any]:
    proof_args = {
        "run_nonce": args.tool_run_nonce,
        "database_identity": args.target_database_identity,
        "require_database_read_only": False,
    }
    before = bound.read_tool_proof(tool_log, args.bound_container_id, args.db, **proof_args)
    with lifecycle_gateway_surface(allow_staging=phase == "T2_stage_pending_proposal"):
        gateway = await bound.invoke_gateway_subprocess(
            _turn_args(args, phase, prompt),
            temp_profile,
            provider_environment=provider_environment,
        )
    after = bound.read_tool_proof(tool_log, args.bound_container_id, args.db, **proof_args)
    new_invocations = after["invocations"][before["invocation_count"] :]
    return {
        "phase": phase,
        "prompt": prompt,
        "prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
        "gateway": gateway,
        "tool_invocations": new_invocations,
    }


def _phase_commands(turn: dict[str, Any]) -> list[str]:
    return [
        str(invocation.get("argv", [None])[0])
        for invocation in turn.get("tool_invocations", [])
        if invocation.get("argv")
    ]


def _phase_argvs(turn: dict[str, Any]) -> list[list[str]]:
    return [
        [str(value) for value in invocation.get("argv", [])]
        for invocation in turn.get("tool_invocations", [])
        if invocation.get("argv")
    ]


def _phase_has_bound_read(turn: dict[str, Any], command: str, row_id: str) -> bool:
    expected = [command, row_id]
    return any(
        argv == expected or (argv[:2] == expected and argv[2:] in (["--format", "json"], ["--format", "markdown"]))
        for argv in _phase_argvs(turn)
    )


def _require_gateway_turn(turn: dict[str, Any]) -> None:
    gateway = turn.get("gateway") or {}
    if gateway.get("handler_error"):
        raise bound.CheckpointError(f"{turn['phase']} handler failed: {gateway['handler_error']}")
    if gateway.get("authorized") is not True or gateway.get("handler_invoked") is not True:
        raise bound.CheckpointError(f"{turn['phase']} did not invoke an authorized GatewayRunner handler")
    if not str(gateway.get("reply") or "").strip():
        raise bound.CheckpointError(f"{turn['phase']} returned an empty reply")


def _command_receipt(result: subprocess.CompletedProcess[str]) -> dict[str, Any]:
    stdout = bound.redact_text(result.stdout or "")
    stderr = bound.redact_text(result.stderr or "")
    return {
        "returncode": result.returncode,
        "stdout_sha256": hashlib.sha256(stdout.encode()).hexdigest(),
        "stderr_sha256": hashlib.sha256(stderr.encode()).hexdigest(),
        "stdout_excerpt": stdout[:2000],
        "stderr_excerpt": stderr[:2000],
        "stdout_truncated": len(stdout) > 2000,
        "stderr_truncated": len(stderr) > 2000,
    }


def _guarded_args(args: argparse.Namespace) -> argparse.Namespace:
    return SimpleNamespace(
        container=args.bound_container_id,
        approve_script=HERE / "approve_proposal.py",
        apply_script=HERE / "apply_proposal.py",
        review_secrets_file=args.review_secrets_file,
        secrets_file=args.apply_secrets_file,
        review_role=DEFAULT_REVIEW_ROLE,
        apply_role=DEFAULT_APPLY_ROLE,
    )


def run_operator_review(
    args: argparse.Namespace, proposal_id: str
) -> tuple[subprocess.CompletedProcess[str], subprocess.CompletedProcess[str]]:
    guard_args = _guarded_args(args)
    dry_run = guarded._approve(guard_args, proposal_id, args.db, dry_run=True)
    if dry_run.returncode != 0 or "kb_stage.approve_strict_proposal" not in dry_run.stdout:
        raise bound.CheckpointError(
            "operator review dry-run did not build the payload-bound approval function: "
            + bound.redact_text(dry_run.stderr or dry_run.stdout)
        )
    applied = guarded._approve(guard_args, proposal_id, args.db)
    if applied.returncode != 0:
        raise bound.CheckpointError("operator review failed: " + bound.redact_text(applied.stderr or applied.stdout))
    return dry_run, applied


def run_guarded_apply(
    args: argparse.Namespace, proposal_id: str
) -> tuple[subprocess.CompletedProcess[str], subprocess.CompletedProcess[str]]:
    guard_args = _guarded_args(args)
    dry_run = guarded._apply(guard_args, proposal_id, args.db, dry_run=True)
    required_signals = ("kb_stage.assert_approved_proposal", "kb_stage.finish_approved_proposal")
    if dry_run.returncode != 0 or not all(signal in dry_run.stdout for signal in required_signals):
        raise bound.CheckpointError(
            "guarded apply dry-run did not build both payload-bound apply guards: "
            + bound.redact_text(dry_run.stderr or dry_run.stdout)
        )
    applied = guarded._apply(guard_args, proposal_id, args.db)
    if applied.returncode != 0:
        raise bound.CheckpointError("guarded apply failed: " + bound.redact_text(applied.stderr or applied.stdout))
    return dry_run, applied


def _table_deltas(before: dict[str, int], after: dict[str, int]) -> dict[str, int]:
    return guarded._table_deltas(before, after)


def _expected_deltas(fixture: dict[str, Any]) -> tuple[dict[str, int], dict[str, int]]:
    canonical = guarded._expected_table_deltas(guarded._expected_bundle_rows(fixture["apply_payload"]))
    canonical[APPROVAL_TABLE] = 0
    from_baseline = dict(canonical)
    from_baseline["kb_stage.kb_proposals"] = 1
    from_baseline[APPROVAL_TABLE] = 1
    return canonical, from_baseline


def _snapshot_matches_production(target: dict[str, Any] | None, production: dict[str, Any] | None) -> bool:
    if not target or not production:
        return False
    return all(
        target.get(section, {}).get(table) == production.get(section, {}).get(table)
        for section in ("counts", "rowset_md5")
        for table in PRODUCTION_TABLES
    )


def _service_unchanged(before: dict[str, Any] | None, after: dict[str, Any] | None) -> bool:
    return bool(before and after and before.get("returncode") == 0 and before == after)


def _reply_mentions(reply: str, value: str) -> bool:
    lowered = reply.lower()
    value = value.lower()
    return value in lowered or value[:8] in lowered


def parse_lifecycle_state(reply: str) -> dict[str, Any]:
    for line in reversed(reply.splitlines()):
        stripped = line.strip()
        if not stripped.startswith(LIFECYCLE_STATE_PREFIX):
            continue
        payload = stripped[len(LIFECYCLE_STATE_PREFIX) :].strip()
        try:
            value = json.loads(payload)
        except json.JSONDecodeError as exc:
            return {"valid": False, "error": f"invalid lifecycle JSON: {exc}"}
        if not isinstance(value, dict):
            return {"valid": False, "error": "lifecycle receipt must be a JSON object"}
        if value.get("phase") not in {"T1", "T2", "T3", "T5"}:
            return {"valid": False, "error": "lifecycle receipt phase is invalid"}
        return {"valid": True, "value": value}
    return {"valid": False, "error": "reply did not contain a LIFECYCLE_STATE line"}


def lifecycle_state_matches(reply: str, expected: dict[str, Any]) -> bool:
    receipt = parse_lifecycle_state(reply)
    return receipt.get("valid") is True and receipt.get("value") == expected


def _reply_has_phase_contradiction(phase: str, reply: str) -> bool:
    prose = "\n".join(
        line for line in reply.lower().splitlines() if not line.strip().startswith(LIFECYCLE_STATE_PREFIX.lower())
    )
    forbidden = {
        "T1": ("staged a proposal", "canonical kb changed", "kb was mutated"),
        "T2": ("already canonical", "now canonical", "became canonical", "status is approved", "status is applied"),
        "T3": ("already canonical", "now canonical", "became canonical", "status is pending", "has been applied"),
        "T5": ("not canonical", "not applied", "status is pending", "status is approved"),
    }
    return any(fragment in prose for fragment in forbidden[phase])


def _all_turns(report: dict[str, Any]) -> list[dict[str, Any]]:
    return [
        report.get("turns", {}).get(name, {})
        for name in (
            "T1_conversation_marker",
            "T2_stage_pending_proposal",
            "T3_approved_unapplied_readback",
            "T5_recall_and_applied_readback",
        )
    ]


def evaluate_checks(report: dict[str, Any], args: argparse.Namespace, fixture: dict[str, Any]) -> dict[str, bool]:
    turns = _all_turns(report)
    gateways = [turn.get("gateway") or {} for turn in turns]
    replies = [str(gateway.get("reply") or "") for gateway in gateways]
    tool_surfaces = [gateway.get("tool_surface") or {} for gateway in gateways]
    children = [gateway.get("child_process") or {} for gateway in gateways]
    terminal_attempts = [
        gateway.get("transcript_terminal_attempts")
        or bound.transcript_terminal_attempt_summary((gateway.get("transcript_tool_trace") or {}).get("events", []))
        for gateway in gateways
    ]
    transcript_calls = [
        event.get("tool_name")
        for gateway in gateways
        for event in (gateway.get("transcript_tool_trace") or {}).get("events", [])
        if event.get("phase") == "call"
    ]
    allowed_tool_names = {name for surface in tool_surfaces for name in (surface.get("allowed_tools") or [])}
    all_commands = [command for turn in turns for command in _phase_commands(turn)]
    pending = report.get("pending_readback")
    approved = report.get("approved_unapplied_readback")
    applied = report.get("final_applied_readback")
    expected_rows = report.get("expected_canonical_rows")
    canonical_rows = report.get("final_canonical_rows")
    production = report.get("production_invariants") or {}
    cleanup = report.get("cleanup") or {}
    restart = report.get("isolated_restart") or {}
    operator_review = report.get("operator_review") or {}
    guarded_apply = report.get("guarded_apply") or {}
    target_deltas = report.get("target_deltas") or {}
    expected_apply_delta, expected_final_delta = _expected_deltas(fixture)
    expected_stage_delta = {table: 0 for table in LIFECYCLE_TABLES}
    expected_stage_delta["kb_stage.kb_proposals"] = 1
    expected_review_delta = {table: 0 for table in LIFECYCLE_TABLES}
    expected_review_delta[APPROVAL_TABLE] = 1
    t3_lower = replies[2].lower() if len(replies) > 2 else ""
    t5_lower = replies[3].lower() if len(replies) > 3 else ""
    expected_receipts = [
        {"phase": "T1", "conversation_marker": args.conversation_marker, "kb_mutated": False},
        {
            "phase": "T2",
            "proposal_id": fixture["proposal_id"],
            "status": "pending_review",
            "canonical": False,
        },
        {
            "phase": "T3",
            "proposal_id": fixture["proposal_id"],
            "status": "approved",
            "applied": False,
            "canonical": False,
        },
        {
            "phase": "T5",
            "conversation_marker": args.conversation_marker,
            "proposal_id": fixture["proposal_id"],
            "status": "applied",
            "claim_id": fixture["claim_id"],
            "source_id": fixture["source_id"],
        },
    ]
    target = report.get("target") or {}
    target_before = target.get("container_identity_before") or {}
    target_after = target.get("container_identity_after") or {}
    target_guard_before = report.get("target_guard_snapshot_before") or {}
    target_guard_after = report.get("target_guard_snapshot_after") or {}
    return {
        "target_container_identity_is_not_production": (
            bool(target_before.get("id"))
            and target_before.get("id") != production.get("container_identity", {}).get("id")
            and args.container != bound.PRODUCTION_CONTAINER
        ),
        "target_container_isolation_contract_passed": bool(target.get("isolation_checks"))
        and all(target.get("isolation_checks", {}).values()),
        "target_container_name_still_resolves_to_pinned_identity": bool(target_after)
        and target_after == target_before
        and target.get("bound_container_id") == target_before.get("id"),
        "target_postgres_system_is_distinct_and_stable": bool(target_guard_before and target_guard_after)
        and target_guard_before.get("database_identity", {}).get("system_identifier")
        != production.get("guard_snapshot_before", {}).get("database_identity", {}).get("system_identifier")
        and target_guard_before.get("database_identity", {}).get("system_identifier")
        == target_guard_after.get("database_identity", {}).get("system_identifier")
        and target_guard_after.get("database_identity", {}).get("current_database") == args.db,
        "clone_baseline_matches_full_production_data": report.get("clone_baseline_matches_production") is True,
        "all_gateway_turns_authorized_and_nonempty": bool(gateways)
        and all(
            gateway.get("authorized") is True
            and gateway.get("handler_invoked") is True
            and not gateway.get("handler_error")
            and bool(str(gateway.get("reply") or "").strip())
            for gateway in gateways
        ),
        "T1_marker_acknowledged_as_conversation_only": (
            args.conversation_marker in replies[0] and "canonical" in replies[0].lower() if replies else False
        ),
        "T1_database_unchanged_and_staging_absent": report.get("target_guard_snapshot_after_t1")
        == report.get("target_guard_snapshot_before")
        and report.get("T1_proposal_readback") is None
        and STAGE_COMMAND not in (_phase_commands(turns[0]) if turns else []),
        "all_lifecycle_state_receipts_exact": len(replies) == len(expected_receipts)
        and all(
            lifecycle_state_matches(reply, expected) for reply, expected in zip(replies, expected_receipts, strict=True)
        ),
        "no_lifecycle_reply_contradicts_its_receipt": len(replies) == len(expected_receipts)
        and all(
            not _reply_has_phase_contradiction(phase, reply)
            for phase, reply in zip(("T1", "T2", "T3", "T5"), replies, strict=True)
        ),
        "T2_model_invoked_exact_staging_verb": [STAGE_COMMAND] in _phase_argvs(turns[1]),
        "T2_model_read_exact_staged_proposal": _phase_has_bound_read(turns[1], "show-proposal", fixture["proposal_id"]),
        "model_never_invoked_review_or_apply": all(
            not re.search(r"(?:^|[-_])(approve|apply)(?:$|[-_])", command) for command in all_commands
        ),
        "all_bridge_invocations_within_read_or_stage_allowlist": bool(all_commands)
        and all(command in READ_ONLY_COMMANDS | STAGING_COMMANDS for command in all_commands),
        "all_bridge_invocations_bound_and_successful": (
            report.get("tool_proof", {}).get("all_bound_to_supplied_target") is True
            and report.get("tool_proof", {}).get("all_completed_successfully") is True
        ),
        "pending_proposal_exact_and_unreviewed": (
            pending is not None
            and pending.get("id") == fixture["proposal_id"]
            and pending.get("payload") == fixture["proposal_payload"]
            and bound._state_semantics(pending, "pending_review")["all"]
        ),
        "pending_state_has_no_canonical_rows": report.get("pending_canonical_rows")
        == {key: [] for key in guarded._expected_bundle_rows(fixture["apply_payload"])},
        "staging_idempotently_rediscovered_one_proposal": (
            report.get("staging_idempotency_replay", {}).get("idempotent_existing") is True
            and report.get("staging_idempotency_replay", {}).get("marker_match_count") == 1
            and report.get("staging_idempotency_replay", {}).get("proposal", {}).get("id") == fixture["proposal_id"]
        ),
        "operator_review_used_payload_bound_security_definer": (
            operator_review.get("dry_run", {}).get("returncode") == 0
            and operator_review.get("dry_run_uses_approve_strict_proposal") is True
            and operator_review.get("apply", {}).get("returncode") == 0
        ),
        "approved_but_unapplied_readback_exact": (
            guarded._approval_transition_valid(pending, approved)
            and bound._state_semantics(approved or {}, "approved")["all"]
            and report.get("approved_canonical_rows")
            == {key: [] for key in guarded._expected_bundle_rows(fixture["apply_payload"])}
        ),
        "immutable_operator_approval_matches_ledger": guarded._approval_snapshot_matches(
            approved, report.get("immutable_approval_readback")
        ),
        "T3_handler_read_approved_but_unapplied_state": (
            _phase_has_bound_read(turns[2], "show-proposal", fixture["proposal_id"])
            and _reply_mentions(replies[2], fixture["proposal_id"])
            and "approved" in t3_lower
            and (
                "not applied" in t3_lower
                or "not canonical" in t3_lower
                or ("applied_at" in t3_lower and "null" in t3_lower)
            )
        ),
        "guarded_apply_used_both_payload_guards": (
            guarded_apply.get("dry_run", {}).get("returncode") == 0
            and guarded_apply.get("dry_run_uses_assert_approved_proposal") is True
            and guarded_apply.get("dry_run_uses_finish_approved_proposal") is True
            and guarded_apply.get("apply", {}).get("returncode") == 0
        ),
        "apply_transition_exact": guarded._apply_transition_valid(approved, applied),
        "final_canonical_rows_exact": canonical_rows == expected_rows,
        "staging_table_deltas_exact": target_deltas.get("baseline_to_stage") == expected_stage_delta,
        "review_created_only_immutable_approval_row": target_deltas.get("stage_to_approval") == expected_review_delta,
        "approval_snapshot_unchanged_during_apply": report.get("immutable_approval_readback")
        == report.get("immutable_approval_readback_after_apply"),
        "apply_table_deltas_exact": target_deltas.get("approved_to_applied") == expected_apply_delta,
        "full_lifecycle_table_deltas_exact": target_deltas.get("baseline_to_final") == expected_final_delta,
        "all_unrelated_clone_rows_unchanged": report.get("target_unrelated_rows_unchanged") is True,
        "isolated_handler_reopened_with_same_session": (
            restart.get("prior_child_absent_before_reopen") is True
            and restart.get("new_child_started") is True
            and restart.get("same_session_key") is True
            and restart.get("same_persisted_session_id") is True
            and restart.get("service_restart_performed") is False
        ),
        "T5_prompt_did_not_repeat_conversation_marker": (
            args.conversation_marker not in (turns[3].get("prompt") or "")
            and args.conversation_marker not in json.dumps(fixture, sort_keys=True)
        ),
        "T5_recalled_marker_and_applied_rows": (
            args.conversation_marker in replies[3]
            and "applied" in t5_lower
            and _reply_mentions(replies[3], fixture["proposal_id"])
            and _reply_mentions(replies[3], fixture["claim_id"])
            and _phase_has_bound_read(turns[3], "show-proposal", fixture["proposal_id"])
            and _phase_has_bound_read(turns[3], "show", fixture["claim_id"])
        ),
        "send_tool_and_delivery_adapters_absent": bool(tool_surfaces)
        and all(
            surface.get("send_message_tool_enabled") is False
            and set(surface.get("allowed_tools") or []) == {"skills_list", "skill_view", "terminal"}
            and set(surface.get("actual_registry_tools") or []) == {"skills_list", "skill_view", "terminal"}
            and surface.get("gateway_adapters_verified_mapping") is True
            and surface.get("gateway_adapter_count") == 0
            and surface.get("model_can_approve_or_apply") is False
            and surface.get("operator_review_apply_tools_exposed") is False
            for surface in tool_surfaces
        ),
        "all_transcript_tool_calls_allowlisted": bool(transcript_calls)
        and all(name in allowed_tool_names for name in transcript_calls),
        "all_gateway_process_groups_removed": bool(children)
        and all(
            child.get("alive_after_readback") is False and child.get("process_group_alive_after_readback") is False
            for child in children
        ),
        "all_gateway_children_ready_and_cleanly_exited": bool(children)
        and all(
            child.get("readiness_verified") is True
            and child.get("timed_out") is False
            and child.get("exitcode") == 0
            and (child.get("termination") or {}).get("attempted") is False
            for child in children
        ),
        "all_gateway_results_used_private_files": bool(children)
        and all(child.get("result_transport") == "private_temp_file" for child in children),
        "all_terminal_calls_passed_first_time": bool(terminal_attempts)
        and all(
            attempt.get("rejected_call_count") == 0
            and attempt.get("nonzero_call_count") == 0
            and attempt.get("result_code_unknown_call_count") == 0
            for attempt in terminal_attempts
        )
        and sum(int(attempt.get("call_count") or 0) for attempt in terminal_attempts)
        == report.get("tool_proof", {}).get("invocation_count"),
        "production_row_fingerprints_unchanged": production.get("guard_snapshot_unchanged") is True,
        "production_service_unchanged": production.get("service_unchanged") is True,
        "live_bridge_unchanged": production.get("live_bridge_unchanged") is True,
        "source_files_unchanged_during_run": report.get("source_files_unchanged_during_run") is True,
        "no_telegram_send": report.get("posted_to_telegram") is False
        and all(gateway.get("posted_to_telegram") is False for gateway in gateways),
        "temporary_profile_and_session_removed": (
            cleanup.get("temp_profile_removed") is True
            and cleanup.get("temp_session_removed_with_profile") is True
            and cleanup.get("absence_verified_independently") is True
            and cleanup.get("active_temp_root_registry_clear") is True
            and cleanup.get("active_gateway_registry_clear") is True
        ),
    }


def validate_args(args: argparse.Namespace) -> None:
    _validate_marker(args.run_marker, "--run-marker")
    _validate_marker(args.conversation_marker, "--conversation-marker")
    if args.run_marker == args.conversation_marker:
        raise bound.CheckpointError("run and conversation markers must be distinct")
    if not bound.SAFE_DOCKER_NAME_RE.fullmatch(args.container):
        raise bound.CheckpointError("--container must be an explicit Docker name")
    if args.container == bound.PRODUCTION_CONTAINER:
        raise bound.CheckpointError("--container must not target the production teleo-pg container")
    if not bound.SAFE_DB_NAME_RE.fullmatch(args.db):
        raise bound.CheckpointError("--db must be an explicit Postgres database name")
    if args.turn_timeout <= 0:
        raise bound.CheckpointError("--turn-timeout must be positive")
    if not args.operator_review:
        raise bound.CheckpointError("--operator-review is required for the explicit harness review phase")
    if not args.guarded_apply:
        raise bound.CheckpointError("--guarded-apply is required for the clone-only apply phase")
    if not args.copy_model_auth:
        raise bound.CheckpointError("--copy-model-auth is required for the isolated live GatewayRunner turns")
    args.output = bound.validate_private_output_path(args.output)


def _source_manifest() -> list[dict[str, Any]]:
    records = []
    for path in (
        Path(__file__).resolve(),
        (HERE / "run_leo_clone_bound_handler_checkpoint.py").resolve(),
        (HERE / "run_approve_claim_clone_canary.py").resolve(),
        (HERE / "approve_proposal.py").resolve(),
        (HERE / "apply_proposal.py").resolve(),
    ):
        content = path.read_bytes()
        records.append(
            {
                "repo_relative_path": path.relative_to(REPO_ROOT.resolve()).as_posix(),
                "sha256": hashlib.sha256(content).hexdigest(),
                "size_bytes": len(content),
            }
        )
    return sorted(records, key=lambda row: row["repo_relative_path"])


async def run_lifecycle(args: argparse.Namespace) -> dict[str, Any]:
    validate_args(args)
    fixture = build_lifecycle_fixture(args.run_marker)
    prompts = lifecycle_prompts(fixture, args.conversation_marker)
    expected_rows = guarded._expected_bundle_rows(fixture["apply_payload"])
    report: dict[str, Any] = {
        "schema": SCHEMA,
        "generated_at_utc": bound.utc_now(),
        "current_canary": (
            "one no-send GatewayRunner lifecycle against an existing disposable full-data Postgres clone"
        ),
        "required_tier": "live_vps_no_send_disposable_full_data_clone",
        "mode": "live_vps_gatewayrunner_clone_stage_review_apply_restart_recall_no_send",
        "posted_to_telegram": False,
        "production_writes_attempted": False,
        "production_service_restart_attempted": False,
        "clone_created_or_dropped": False,
        "target": {"container": args.container, "database": args.db},
        "markers": {
            "run_marker": args.run_marker,
            "conversation_marker": args.conversation_marker,
            "conversation_marker_persisted_in_mutation_payload": False,
        },
        "fixture_ids": {
            "proposal_id": fixture["proposal_id"],
            "claim_id": fixture["claim_id"],
            "source_id": fixture["source_id"],
            "apply_payload_sha256": _json_hash(fixture["apply_payload"]),
            "proposal_payload_sha256": _json_hash(fixture["proposal_payload"]),
        },
        "model_authority": {
            "read_commands": sorted(READ_ONLY_COMMANDS),
            "staging_commands": sorted(STAGING_COMMANDS),
            "can_approve": False,
            "can_apply": False,
            "can_send": False,
        },
        "operator_authority": {
            "review_requested": args.operator_review,
            "guarded_apply_requested": args.guarded_apply,
            "reviewer_handle": REVIEWER_HANDLE,
            "applied_by_handle": APPLIED_BY_HANDLE,
            "target_pinned_to_supplied_clone": True,
        },
        "source_manifest_before": _source_manifest(),
        "expected_canonical_rows": expected_rows,
        "turns": {},
        "errors": [],
        "output": str(args.output),
    }

    temp_profile: Path | None = None
    tool_log: Path | None = None
    provider_environment: dict[str, str] = {}
    production_guard_before: dict[str, Any] | None = None
    production_service_before: dict[str, Any] | None = None
    live_bridge_before: dict[str, str | None] | None = None
    target_guard_before: dict[str, Any] | None = None
    target_base_guard_before: dict[str, Any] | None = None
    target_unrelated_before: dict[str, Any] | None = None
    target_container_id: str | None = None
    target_identity_after: dict[str, Any] | None = None
    bridge: dict[str, Any] = {}
    provider_secret_values: tuple[str, ...] = ()

    try:
        target_identity, production_identity = assert_disposable_target(args.container, args.db)
        target_container_id = str(target_identity["id"])
        args.bound_container_id = target_container_id
        report["target"].update(
            {
                "requested_container": args.container,
                "bound_container_id": target_container_id,
                "container_identity_before": target_identity,
                "isolation_checks": bound.validate_disposable_target_identity(target_identity, production_identity),
            }
        )
        report["production_invariants"] = {"container_identity": production_identity}
        production_guard_before = bound.database_guard_snapshot(
            bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB, tables=PRODUCTION_TABLES
        )
        production_service_before = bound.service_state()
        live_bridge_before = bound.bridge_hashes()
        target_base_guard_before = bound.database_guard_snapshot(
            target_container_id, args.db, tables=PRODUCTION_TABLES
        )
        target_guard_before = bound.database_guard_snapshot(target_container_id, args.db, tables=LIFECYCLE_TABLES)
        args.target_database_identity = target_guard_before["database_identity"]
        if target_guard_before.get("database_identity", {}).get("system_identifier") == production_guard_before.get(
            "database_identity", {}
        ).get("system_identifier"):
            raise bound.CheckpointError("target and production resolve to the same Postgres system identifier")
        report["production_invariants"].update(
            {
                "guard_snapshot_before": production_guard_before,
                "service_before": production_service_before,
                "live_bridge_hashes_before": live_bridge_before,
            }
        )
        report["target_guard_snapshot_before"] = target_guard_before
        report["target_base_guard_snapshot_before"] = target_base_guard_before
        report["clone_baseline_matches_production"] = _snapshot_matches_production(
            target_base_guard_before, production_guard_before
        )
        if not report["clone_baseline_matches_production"]:
            raise bound.CheckpointError("target is not an exact full-data clone of production across base tables")
        if guarded._proposal_readback(target_container_id, args.db, fixture["proposal_id"]) is not None:
            raise bound.CheckpointError("run marker is not unique: deterministic proposal already exists")
        if guarded._exact_bundle_readback(target_container_id, args.db, fixture["apply_payload"]) != {
            key: [] for key in expected_rows
        }:
            raise bound.CheckpointError("run marker is not unique: deterministic canonical rows already exist")
        target_unrelated_before = target_unrelated_guard_snapshot(target_container_id, args.db, fixture)
        report["target_unrelated_guard_before"] = target_unrelated_before
        missing_secrets = [
            label
            for label, path in (
                ("review", Path(args.review_secrets_file)),
                ("apply", Path(args.apply_secrets_file)),
            )
            if not path.is_file()
        ]
        if missing_secrets:
            raise bound.CheckpointError(
                "required separated clone credential file(s) are absent: " + ", ".join(missing_secrets)
            )

        temp_profile, copy_audit = bound.copy_profile(
            run_id=args.run_marker,
            prompt_id="lifecycle",
        )
        report["temporary_profile_copy_audit"] = copy_audit
        report["model_auth_binding"] = bound.copy_ephemeral_model_auth(temp_profile)
        provider_environment, environment_audit = bound.load_ephemeral_provider_environment(temp_profile)
        provider_secret_values = tuple(provider_environment.values())
        report["model_auth_binding"]["environment_binding"] = environment_audit
        bridge = patch_lifecycle_bridge(
            temp_profile,
            container=target_container_id,
            database=args.db,
            fixture=fixture,
        )
        report["temporary_bridge"] = bridge
        tool_log = Path(bridge["tool_log_path"])
        args.tool_run_nonce = bridge["run_nonce"]

        with lifecycle_gateway_surface(allow_staging=False):
            t1 = await run_gateway_turn(
                args,
                temp_profile,
                phase="T1_conversation_marker",
                prompt=prompts["T1_conversation_marker"],
                provider_environment=provider_environment,
                tool_log=tool_log,
            )
            _require_gateway_turn(t1)
            report["turns"]["T1_conversation_marker"] = t1
            target_guard_after_t1 = bound.database_guard_snapshot(target_container_id, args.db, tables=LIFECYCLE_TABLES)
            report["target_guard_snapshot_after_t1"] = target_guard_after_t1
            report["T1_proposal_readback"] = guarded._proposal_readback(
                target_container_id, args.db, fixture["proposal_id"]
            )
            if target_guard_after_t1 != target_guard_before or report["T1_proposal_readback"] is not None:
                raise bound.CheckpointError("T1 changed clone database state before staging was authorized")

            t2 = await run_gateway_turn(
                args,
                temp_profile,
                phase="T2_stage_pending_proposal",
                prompt=prompts["T2_stage_pending_proposal"],
                provider_environment=provider_environment,
                tool_log=tool_log,
            )
            _require_gateway_turn(t2)
            report["turns"]["T2_stage_pending_proposal"] = t2
            if STAGE_COMMAND not in _phase_commands(t2):
                raise bound.CheckpointError("T2 did not invoke the staging-only lifecycle verb")

            pending = guarded._proposal_readback(target_container_id, args.db, fixture["proposal_id"])
            report["pending_readback"] = pending
            if not pending or not bound._state_semantics(pending, "pending_review")["all"]:
                raise bound.CheckpointError("T2 did not leave the exact proposal pending and unapplied")
            report["pending_canonical_rows"] = guarded._exact_bundle_readback(
                target_container_id, args.db, fixture["apply_payload"]
            )
            report["staging_idempotency_replay"] = stage_lifecycle_proposal(target_container_id, args.db, fixture)
            counts_after_stage = bound.database_counts(target_container_id, args.db, tables=LIFECYCLE_TABLES)
            report["target_counts_after_stage"] = counts_after_stage

            review_dry_run, review_apply = run_operator_review(args, fixture["proposal_id"])
            approved = guarded._proposal_readback(target_container_id, args.db, fixture["proposal_id"])
            approval_snapshot = guarded._approval_snapshot_readback(
                target_container_id, args.db, fixture["proposal_id"]
            )
            approved_rows = guarded._exact_bundle_readback(target_container_id, args.db, fixture["apply_payload"])
            report["operator_review"] = {
                "controller": "explicit_harness_operator_phase",
                "reviewed_by": REVIEWER_HANDLE,
                "review_note": REVIEW_NOTE,
                "dry_run": _command_receipt(review_dry_run),
                "dry_run_uses_approve_strict_proposal": ("kb_stage.approve_strict_proposal" in review_dry_run.stdout),
                "apply": _command_receipt(review_apply),
            }
            report["approved_unapplied_readback"] = approved
            report["immutable_approval_readback"] = approval_snapshot
            report["approved_canonical_rows"] = approved_rows
            report["target_counts_after_approval"] = bound.database_counts(
                target_container_id, args.db, tables=LIFECYCLE_TABLES
            )
            if not guarded._approval_transition_valid(pending, approved):
                raise bound.CheckpointError("operator review did not produce the exact approved transition")
            if approved_rows != {key: [] for key in expected_rows}:
                raise bound.CheckpointError("canonical rows appeared before guarded apply")

            t3 = await run_gateway_turn(
                args,
                temp_profile,
                phase="T3_approved_unapplied_readback",
                prompt=prompts["T3_approved_unapplied_readback"],
                provider_environment=provider_environment,
                tool_log=tool_log,
            )
            _require_gateway_turn(t3)
            report["turns"]["T3_approved_unapplied_readback"] = t3

            apply_dry_run, apply_execute = run_guarded_apply(args, fixture["proposal_id"])
            report["guarded_apply"] = {
                "controller": "explicit_harness_operator_phase",
                "applied_by": APPLIED_BY_HANDLE,
                "dry_run": _command_receipt(apply_dry_run),
                "dry_run_uses_assert_approved_proposal": ("kb_stage.assert_approved_proposal" in apply_dry_run.stdout),
                "dry_run_uses_finish_approved_proposal": ("kb_stage.finish_approved_proposal" in apply_dry_run.stdout),
                "apply": _command_receipt(apply_execute),
            }
            applied = guarded._proposal_readback(target_container_id, args.db, fixture["proposal_id"])
            approval_snapshot_after_apply = guarded._approval_snapshot_readback(
                target_container_id, args.db, fixture["proposal_id"]
            )
            final_rows = guarded._exact_bundle_readback(target_container_id, args.db, fixture["apply_payload"])
            final_counts = bound.database_counts(target_container_id, args.db, tables=LIFECYCLE_TABLES)
            report["final_applied_readback"] = applied
            report["immutable_approval_readback_after_apply"] = approval_snapshot_after_apply
            report["final_canonical_rows"] = final_rows
            report["target_counts_final"] = final_counts
            report["target_deltas"] = {
                "baseline_to_stage": _table_deltas(target_guard_before["counts"], counts_after_stage),
                "stage_to_approval": _table_deltas(counts_after_stage, report["target_counts_after_approval"]),
                "approved_to_applied": _table_deltas(report["target_counts_after_approval"], final_counts),
                "baseline_to_final": _table_deltas(target_guard_before["counts"], final_counts),
            }
            if not guarded._apply_transition_valid(approved, applied):
                raise bound.CheckpointError("guarded apply did not produce the exact applied transition")
            if approval_snapshot_after_apply != approval_snapshot:
                raise bound.CheckpointError("guarded apply changed the immutable approval snapshot")
            if final_rows != expected_rows:
                raise bound.CheckpointError("final canonical row projection does not match the reviewed payload")

            t3_child = t3.get("gateway", {}).get("child_process") or {}
            t5 = await run_gateway_turn(
                args,
                temp_profile,
                phase="T5_recall_and_applied_readback",
                prompt=prompts["T5_recall_and_applied_readback"],
                provider_environment=provider_environment,
                tool_log=tool_log,
            )
            _require_gateway_turn(t5)
            report["turns"]["T5_recall_and_applied_readback"] = t5
            t5_child = t5.get("gateway", {}).get("child_process") or {}
            t3_gateway = t3.get("gateway") or {}
            t5_gateway = t5.get("gateway") or {}
            report["isolated_restart"] = {
                "kind": "new GatewayRunner child process reopening the same private persisted session",
                "service_restart_performed": False,
                "prior_child_pid": t3_child.get("pid"),
                "new_child_pid": t5_child.get("pid"),
                "prior_child_absent_before_reopen": (
                    t3_child.get("alive_after_readback") is False
                    and t3_child.get("process_group_alive_after_readback") is False
                ),
                "new_child_started": bool(t5_child.get("pid")),
                "child_pid_changed": t3_child.get("pid") != t5_child.get("pid"),
                "same_session_key": t3_gateway.get("session_key") == t5_gateway.get("session_key")
                and bool(t3_gateway.get("session_key")),
                "same_persisted_session_id": (
                    (t3_gateway.get("transcript_tool_trace") or {}).get("session_id")
                    == (t5_gateway.get("transcript_tool_trace") or {}).get("session_id")
                    and bool((t3_gateway.get("transcript_tool_trace") or {}).get("session_id"))
                ),
            }
    except Exception as exc:
        report["errors"].append({"type": type(exc).__name__, "message": str(exc)})
        report["traceback_tail"] = traceback.format_exc().splitlines()[-16:]
    finally:
        provider_environment.clear()
        child_cleanup = bound.cleanup_active_gateway_children()
        try:
            if not tool_log or not target_container_id or not target_guard_before or not bridge.get("run_nonce"):
                raise bound.CheckpointError("tool proof inputs were not established")
            tool_proof = bound.read_tool_proof(
                tool_log,
                target_container_id,
                args.db,
                run_nonce=bridge["run_nonce"],
                database_identity=target_guard_before["database_identity"],
                require_database_read_only=False,
            )
        except Exception as exc:
            tool_proof = {
                "parse_errors": [str(exc)],
                "event_count": 0,
                "duplicate_invocation_ids": [],
                "complete_start_end_pairing": False,
                "invocations": [],
                "invocation_count": 0,
                "all_bound_to_supplied_target": False,
                "database_read_only_required": False,
                "all_completed_successfully": False,
            }
            report["errors"].append({"type": type(exc).__name__, "message": f"tool proof readback: {exc}"})
        report["tool_proof"] = tool_proof
        temp_root = temp_profile.parent if temp_profile else None
        child_cleanup_clear = all(
            item.get("alive_after_cleanup") is False and item.get("process_group_alive_after_cleanup") is False
            for item in child_cleanup.values()
        )
        active_gateway_registry_clear = not bound._ACTIVE_GATEWAY_CHILDREN
        temp_removed = bound.remove_tree(temp_root) if child_cleanup_clear and active_gateway_registry_clear else False
        absence_verified = bound.temp_path_absent(temp_root)
        report["cleanup"] = {
            "active_gateway_child_cleanup": child_cleanup,
            "temp_root": str(temp_root) if temp_root else None,
            "temp_profile_removed": temp_removed,
            "temp_session_removed_with_profile": temp_removed,
            "absence_verified_independently": absence_verified,
            "active_temp_root_registry_clear": (temp_root not in bound._ACTIVE_TEMP_ROOTS if temp_root else True),
            "active_gateway_registry_clear": active_gateway_registry_clear,
            "termination_signal": bound._LAST_TERMINATION_SIGNAL,
        }

        if target_container_id:
            try:
                target_identity_after = bound.container_identity(args.container)
                if target_identity_after.get("id") != target_container_id:
                    raise bound.CheckpointError("target name no longer resolves to the pinned container identity")
                production_identity = report.get("production_invariants", {}).get("container_identity") or {}
                bound.validate_disposable_target_identity(target_identity_after, production_identity)
                report["target"]["container_identity_after"] = target_identity_after
            except Exception as exc:
                report["errors"].append({"type": type(exc).__name__, "message": f"target identity postflight: {exc}"})
            try:
                target_guard_after = bound.database_guard_snapshot(
                    target_container_id, args.db, tables=LIFECYCLE_TABLES
                )
                report["target_guard_snapshot_after"] = target_guard_after
            except Exception as exc:
                report["errors"].append({"type": type(exc).__name__, "message": f"target postflight: {exc}"})
            try:
                target_unrelated_after = target_unrelated_guard_snapshot(target_container_id, args.db, fixture)
                report["target_unrelated_guard_after"] = target_unrelated_after
                report["target_unrelated_rows_unchanged"] = (
                    target_unrelated_before is not None and target_unrelated_before == target_unrelated_after
                )
            except Exception as exc:
                report["target_unrelated_rows_unchanged"] = False
                report["errors"].append(
                    {"type": type(exc).__name__, "message": f"target unrelated-row postflight: {exc}"}
                )
        else:
            report["target_unrelated_rows_unchanged"] = False
        try:
            production_guard_after = bound.database_guard_snapshot(
                bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB, tables=PRODUCTION_TABLES
            )
        except Exception as exc:
            production_guard_after = None
            report["errors"].append({"type": type(exc).__name__, "message": f"production postflight: {exc}"})
        production_service_after = bound.service_state()
        live_bridge_after = bound.bridge_hashes()
        production = report.setdefault("production_invariants", {})
        production.update(
            {
                "guard_snapshot_before": production_guard_before,
                "guard_snapshot_after": production_guard_after,
                "guard_snapshot_unchanged": production_guard_before is not None
                and production_guard_before == production_guard_after,
                "service_before": production_service_before,
                "service_after": production_service_after,
                "service_unchanged": _service_unchanged(production_service_before, production_service_after),
                "live_bridge_hashes_before": live_bridge_before,
                "live_bridge_hashes_after": live_bridge_after,
                "live_bridge_unchanged": live_bridge_before is not None and live_bridge_before == live_bridge_after,
            }
        )
        try:
            report["source_manifest_after"] = _source_manifest()
            report["source_files_unchanged_during_run"] = (
                report["source_manifest_before"] == report["source_manifest_after"]
            )
        except Exception as exc:
            report["errors"].append({"type": type(exc).__name__, "message": f"source manifest postflight: {exc}"})

        report["checks"] = evaluate_checks(report, args, fixture)
        report["status"] = "pass" if not report["errors"] and all(report["checks"].values()) else "fail"
        report["current_tier"] = (
            "live_vps_no_send_disposable_full_data_clone" if report["status"] == "pass" else "partial_or_failed"
        )
        report["claim_ceiling"] = (
            "A passing receipt proves the real VPS GatewayRunner staged one deterministic pending proposal, "
            "the harness alone reviewed and guarded-applied it in the supplied disposable full-data clone, "
            "a reopened isolated handler recalled a conversation-only marker and read applied canonical rows, "
            "and production fingerprints/service/bridge plus no-send cleanup invariants stayed unchanged. "
            "It does not prove Telegram delivery or production mutation."
        )
        report["completed_at_utc"] = bound.utc_now()
        report = bound.redact_value(report, secret_values=provider_secret_values)
        bound.write_report(args.output, report, secret_values=provider_secret_values)
    return report


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--container", required=True, help="Existing disposable Postgres container name.")
    parser.add_argument("--db", required=True, help="Full-data database inside the disposable container.")
    parser.add_argument("--output", required=True, type=Path, help="Private JSON receipt path.")
    parser.add_argument("--run-marker", default=None)
    parser.add_argument("--conversation-marker", default=None)
    parser.add_argument("--chat-id", default=bound.DEFAULT_CHAT_ID)
    parser.add_argument("--user-id", default=bound.DEFAULT_USER_ID)
    parser.add_argument("--user-name", default="codex clone lifecycle checkpoint")
    parser.add_argument("--turn-timeout", type=int, default=300)
    parser.add_argument("--review-secrets-file", default=DEFAULT_REVIEW_SECRETS_FILE)
    parser.add_argument("--apply-secrets-file", default=DEFAULT_APPLY_SECRETS_FILE)
    parser.add_argument(
        "--copy-model-auth",
        action="store_true",
        help="Copy only auth.json into the private temporary profile, then remove it.",
    )
    parser.add_argument(
        "--operator-review",
        action="store_true",
        help="Explicitly allow the harness-owned kb_review phase in the supplied clone.",
    )
    parser.add_argument(
        "--guarded-apply",
        action="store_true",
        help="Explicitly allow the harness-owned kb_apply phase in the supplied clone.",
    )
    args = parser.parse_args(argv)
    args.run_marker = args.run_marker or new_marker("leo-lifecycle")
    args.conversation_marker = args.conversation_marker or new_marker("conversation-memory")
    return args


def parse_internal_stage_args(argv: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("--container", required=True)
    parser.add_argument("--db", required=True)
    parser.add_argument("--run-marker", required=True)
    return parser.parse_args(argv)


def internal_stage_main(argv: list[str]) -> int:
    args = parse_internal_stage_args(argv)
    try:
        _validate_marker(args.run_marker, "--run-marker")
        fixture = build_lifecycle_fixture(args.run_marker)
        result = stage_lifecycle_proposal(args.container, args.db, fixture)
    except Exception as exc:
        print(json.dumps({"status": "rejected", "error": bound.redact_text(str(exc))}, sort_keys=True))
        return 2
    print(json.dumps({"status": "pending_review", **result}, sort_keys=True))
    return 0


def main(argv: list[str] | None = None) -> int:
    raw_argv = list(sys.argv[1:] if argv is None else argv)
    if raw_argv and raw_argv[0] == INTERNAL_STAGE_COMMAND:
        return internal_stage_main(raw_argv[1:])
    args = parse_args(raw_argv)
    try:
        with bound.termination_cleanup_handlers():
            report = asyncio.run(run_lifecycle(args))
    except bound.CheckpointError as exc:
        print(json.dumps({"status": "rejected", "error": str(exc)}, sort_keys=True))
        return 2
    except bound.TerminationRequested as exc:
        print(
            json.dumps(
                {"status": "terminated", "signal": exc.signum, "output": str(args.output)},
                sort_keys=True,
            )
        )
        return 128 + exc.signum
    print(
        json.dumps(
            {
                "status": report.get("status"),
                "output": str(args.output),
                "checks_passed": sum(bool(value) for value in report.get("checks", {}).values()),
                "checks_total": len(report.get("checks", {})),
                "error_count": len(report.get("errors", [])),
            },
            sort_keys=True,
        )
    )
    return 0 if report["status"] == "pass" else 1


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