# -*- coding: utf-8 -*-
"""
TEITAN SELF-REPAIR V1 — FASE 1 SCHEMAS
Data structures for Incident Observation, Read-Only Evidence Collection,
Circuit Tracing, Root Cause Analysis, and Repair Proposal.
"""
from __future__ import annotations

import uuid
from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional


def _utc_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _new_id(prefix: str = "sr") -> str:
    return f"{prefix}_{uuid.uuid4().hex[:12]}"


class IncidentFailureType(str, Enum):
    EXCEPTION = "EXCEPTION"
    FAILED_VERIFICATION = "FAILED_VERIFICATION"
    FAILED_TEST = "FAILED_TEST"
    CONTRACT_CONTRADICTION = "CONTRACT_CONTRADICTION"
    UNEXPECTED_PROCESS_STATE = "UNEXPECTED_PROCESS_STATE"
    UNEXPECTED_PORT_STATE = "UNEXPECTED_PORT_STATE"
    MISSION_FAILURE = "MISSION_FAILURE"
    FALSE_SUCCESS = "FALSE_SUCCESS"

    @classmethod
    def from_str(cls, val: str) -> "IncidentFailureType":
        if isinstance(val, cls):
            return val
        up = str(val or "").strip().upper().replace(" ", "_").replace("-", "_")
        for member in cls:
            if member.value == up or member.name == up:
                return member
        return cls.EXCEPTION


class CircuitStatus(str, Enum):
    PASS = "PASS"
    FAIL = "FAIL"
    NOT_REACHED = "NOT_REACHED"
    UNKNOWN = "UNKNOWN"


CANONICAL_CIRCUIT_HIGHWAY = [
    "creator_input",
    "chat_gateway",
    "brain",
    "requirement_contract",
    "planner",
    "executor",
    "effector",
    "filesystem_runtime_browser",
    "verification",
    "final_mission_state",
]


@dataclass
class CircuitNode:
    component: str
    status: CircuitStatus = CircuitStatus.UNKNOWN
    evidence: List[Dict[str, Any]] = field(default_factory=list)
    details: str = ""

    def to_dict(self) -> Dict[str, Any]:
        return {
            "component": self.component,
            "status": self.status.value if isinstance(self.status, CircuitStatus) else str(self.status),
            "evidence": list(self.evidence),
            "details": self.details,
        }


@dataclass
class IncidentRecord:
    incident_id: str = field(default_factory=lambda: _new_id("inc"))
    timestamp: str = field(default_factory=_utc_iso)
    mission_id: Optional[str] = None
    instance_id: Optional[str] = None
    failure_type: IncidentFailureType = IncidentFailureType.EXCEPTION
    observed_symptom: str = ""
    source_component: str = "unknown"
    initial_evidence: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "incident_id": self.incident_id,
            "timestamp": self.timestamp,
            "mission_id": self.mission_id,
            "instance_id": self.instance_id,
            "failure_type": self.failure_type.value if isinstance(self.failure_type, IncidentFailureType) else str(self.failure_type),
            "observed_symptom": self.observed_symptom,
            "source_component": self.source_component,
            "initial_evidence": dict(self.initial_evidence),
        }


@dataclass
class EvidenceBundle:
    incident_id: str
    logs: List[str] = field(default_factory=list)
    tracebacks: List[str] = field(default_factory=list)
    mission_state: Dict[str, Any] = field(default_factory=dict)
    action_history: List[Dict[str, Any]] = field(default_factory=list)
    verification_results: Dict[str, Any] = field(default_factory=dict)
    relevant_config: Dict[str, Any] = field(default_factory=dict)
    source_excerpts: List[Dict[str, Any]] = field(default_factory=list)
    process_state: Dict[str, Any] = field(default_factory=dict)
    port_state: Dict[str, Any] = field(default_factory=dict)
    hashes: Dict[str, str] = field(default_factory=dict)
    workspace_state: Dict[str, Any] = field(default_factory=dict)
    target_file: Optional[str] = None
    target_files: List[str] = field(default_factory=list)
    source_files: List[str] = field(default_factory=list)
    collected_at: str = field(default_factory=_utc_iso)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class CircuitTrace:
    incident_id: str
    nodes: List[CircuitNode] = field(default_factory=list)
    trace_complete: bool = False
    first_proven_break: Optional[str] = None
    downstream_not_reached: List[str] = field(default_factory=list)
    contradictions: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "incident_id": self.incident_id,
            "nodes": [n.to_dict() for n in self.nodes],
            "trace_complete": self.trace_complete,
            "first_proven_break": self.first_proven_break,
            "downstream_not_reached": list(self.downstream_not_reached),
            "contradictions": list(self.contradictions),
        }


@dataclass
class RootCauseClassification:
    incident_id: str
    symptom: str
    direct_failure: str
    root_cause: str  # e.g. "app/remediation/self_repair/circuit.py::CircuitTracer.trace" or "UNPROVEN"
    sibling_failure: Optional[str] = None
    downstream_effect: Optional[str] = None
    proven: bool = False
    evidence_citations: List[Dict[str, Any]] = field(default_factory=list)
    reasoning: str = ""
    observed_mission_stage: str = "unknown"
    defective_module: Optional[str] = None
    responsible_function: Optional[str] = None
    defect_summary: Optional[str] = None
    downstream_circuit_effects: List[str] = field(default_factory=list)
    confidence: str = "HIGH"

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class RepairProposal:
    repair_id: str = field(default_factory=lambda: _new_id("rep"))
    incident_id: str = ""
    root_cause: str = "UNPROVEN"
    repair_authorized: bool = False
    files_allowed: List[str] = field(default_factory=list)
    files_forbidden: List[str] = field(default_factory=list)
    exact_change_scope: str = ""
    expected_effect: str = ""
    regression_risks: List[str] = field(default_factory=list)
    focused_tests: List[str] = field(default_factory=list)
    regression_tests: List[str] = field(default_factory=list)
    rollback_plan: Dict[str, Any] = field(default_factory=dict)
    confidence: float = 0.0
    sandbox_contract: Dict[str, Any] = field(default_factory=dict)
    production_write_authority: bool = False
    core_write_authority: bool = False
    auto_promotion: bool = False

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


# ==============================================================================
# FASE 2 SCHEMAS: SANDBOX REPAIR & CANDIDATE EVALUATION
# ==============================================================================

class PatchOperationType(str, Enum):
    REPLACE_EXACT = "REPLACE_EXACT"
    INSERT_AFTER = "INSERT_AFTER"
    INSERT_BEFORE = "INSERT_BEFORE"


@dataclass
class PatchInstruction:
    file_rel_path: str
    operation: PatchOperationType
    before: str
    after: str
    reason: str = ""

    def to_dict(self) -> Dict[str, Any]:
        return {
            "file_rel_path": self.file_rel_path,
            "operation": self.operation.value if isinstance(self.operation, PatchOperationType) else str(self.operation),
            "before": self.before,
            "after": self.after,
            "reason": self.reason,
        }


@dataclass
class PatchPlan:
    patch_plan_id: str = field(default_factory=lambda: _new_id("plan"))
    incident_id: str = ""
    repair_id: str = ""
    candidate_id: str = ""
    instructions: List[PatchInstruction] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "patch_plan_id": self.patch_plan_id,
            "incident_id": self.incident_id,
            "repair_id": self.repair_id,
            "candidate_id": self.candidate_id,
            "instructions": [inst.to_dict() for inst in self.instructions],
        }


@dataclass
class PatchResult:
    patch_plan_id: str
    incident_id: str
    repair_id: str
    candidate_id: str
    status: str  # SUCCESS | FAIL
    applied_instructions: List[Dict[str, Any]] = field(default_factory=list)
    failed_instruction: Optional[Dict[str, Any]] = None
    error_message: Optional[str] = None
    file_diffs: Dict[str, str] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class SourceHashRecord:
    relative_path: str
    production_path: str
    source_sha256: str
    candidate_initial_sha256: str

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class SandboxManifest:
    incident_id: str
    repair_id: str
    candidate_id: str
    sandbox_root: str
    source_dir: str
    candidate_dir: str
    files_copied: List[Dict[str, Any]] = field(default_factory=list)
    source_hashes: List[SourceHashRecord] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "incident_id": self.incident_id,
            "repair_id": self.repair_id,
            "candidate_id": self.candidate_id,
            "sandbox_root": self.sandbox_root,
            "source_dir": self.source_dir,
            "candidate_dir": self.candidate_dir,
            "files_copied": list(self.files_copied),
            "source_hashes": [sh.to_dict() for sh in self.source_hashes],
        }


@dataclass
class VerificationResult:
    candidate_id: str
    incident_id: str
    repair_id: str
    compile_pass: bool = True
    compile_errors: List[str] = field(default_factory=list)
    candidate_before_sha256: Dict[str, str] = field(default_factory=dict)
    candidate_after_sha256: Dict[str, str] = field(default_factory=dict)
    focused_tests_pass: bool = True
    focused_tests_summary: Dict[str, Any] = field(default_factory=dict)
    regression_tests_pass: bool = True
    regression_tests_summary: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class ComparisonResult:
    candidate_id: str
    incident_id: str
    repair_id: str
    original_failure: str
    candidate_result: str
    new_failures: int = 0
    regressions: List[str] = field(default_factory=list)
    unchanged_failures: List[str] = field(default_factory=list)
    resolved_failures: List[str] = field(default_factory=list)
    comparison_summary: str = ""

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class CandidateVerdict:
    candidate_id: str
    incident_id: str
    repair_id: str
    verdict: str  # ACCEPT | REJECT
    root_cause_proven: bool = False
    patch_applied: bool = False
    compile_pass: bool = False
    focused_tests_pass: bool = False
    regression_tests_pass: bool = False
    new_failures: int = 0
    production_untouched: bool = True
    baseline_integrity: bool = True
    reasons: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class PromotionProposal:
    incident_id: str
    repair_id: str
    candidate_id: str
    files_changed: List[str] = field(default_factory=list)
    production_sha256_before: Dict[str, str] = field(default_factory=dict)
    candidate_sha256_after: Dict[str, str] = field(default_factory=dict)
    root_cause: str = ""
    change_summary: str = ""
    focused_tests: List[str] = field(default_factory=list)
    regression_tests: List[str] = field(default_factory=list)
    risk: str = "LOW"
    rollback_plan: Dict[str, Any] = field(default_factory=dict)
    candidate_verdict: str = "ACCEPT"
    creator_approval_required: bool = True

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


# ==============================================================================
# FASE 3 SCHEMAS: REAL FAILURE INGESTION & AUTO-REPAIR
# ==============================================================================

@dataclass
class MissionFailureEvent:
    mission_id: str
    incident_id: Optional[str] = None
    instance_id: Optional[str] = None
    task_id: Optional[str] = None
    timestamp: str = field(default_factory=_utc_iso)
    workspace_root: str = ""
    failure_stage: str = "unknown"
    observed_symptom: str = ""
    exception_type: Optional[str] = None
    exception_message: Optional[str] = None
    source_component: str = "unknown"
    execution_history: List[Dict[str, Any]] = field(default_factory=list)
    verification_result: Optional[Dict[str, Any]] = None
    evidence_locations: Dict[str, str] = field(default_factory=dict)
    mission_status: str = "FAILED"
    error_context: Dict[str, Any] = field(default_factory=dict)
    target_file: Optional[str] = None
    target_files: List[str] = field(default_factory=list)
    source_files: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


# ==============================================================================
# FASE 3B SCHEMAS: AUTONOMOUS PATCH REASONING
# ==============================================================================

@dataclass
class PatchCandidate:
    candidate_patch_id: str
    file_rel_path: str
    operation: PatchOperationType
    before: str
    after: str
    reason: str
    confidence: str = "HIGH"  # HIGH | MEDIUM | LOW | UNPROVEN
    evidence_refs: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "candidate_patch_id": self.candidate_patch_id,
            "file_rel_path": self.file_rel_path,
            "operation": self.operation.value if isinstance(self.operation, PatchOperationType) else str(self.operation),
            "before": self.before,
            "after": self.after,
            "reason": self.reason,
            "confidence": self.confidence,
            "evidence_refs": list(self.evidence_refs),
        }


@dataclass
class PatchReasoningResult:
    success: bool
    confidence: str = "UNPROVEN"  # HIGH | MEDIUM | LOW | UNPROVEN
    reasoning_summary: str = ""
    target_file: Optional[str] = None
    candidate_instructions: List[PatchInstruction] = field(default_factory=list)
    candidates: List[PatchCandidate] = field(default_factory=list)
    evidence_used: List[str] = field(default_factory=list)
    rejected_alternatives: List[str] = field(default_factory=list)
    failure_reason: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        return {
            "success": self.success,
            "confidence": self.confidence,
            "reasoning_summary": self.reasoning_summary,
            "target_file": self.target_file,
            "candidate_instructions": [inst.to_dict() for inst in self.candidate_instructions],
            "candidates": [c.to_dict() for c in self.candidates],
            "evidence_used": list(self.evidence_used),
            "rejected_alternatives": list(self.rejected_alternatives),
            "failure_reason": self.failure_reason,
        }


# ==============================================================================
# FASE 4 SCHEMAS: REAL TEITAN SHADOW INTEGRATION
# ==============================================================================

@dataclass
class MissionFailureSnapshot:
    mission_id: str
    instance_id: Optional[str] = None
    workspace_root: str = ""
    creator_request: str = ""
    mission_status: str = "FAILED"
    failure_stage: str = "unknown"
    source_component: str = "unknown"
    planner_result: Optional[Dict[str, Any]] = None
    executor_result: Optional[Dict[str, Any]] = None
    effector_result: Optional[Dict[str, Any]] = None
    verification_result: Optional[Dict[str, Any]] = None
    final_mission_state: Dict[str, Any] = field(default_factory=dict)
    action_history: List[Dict[str, Any]] = field(default_factory=list)
    execution_evidence: Dict[str, Any] = field(default_factory=dict)
    logs: List[str] = field(default_factory=list)
    tracebacks: List[str] = field(default_factory=list)
    source_files: List[str] = field(default_factory=list)
    timestamps: Dict[str, str] = field(default_factory=dict)
    observed_symptom: str = ""
    exception_type: Optional[str] = None
    exception_message: Optional[str] = None
    error_context: Dict[str, Any] = field(default_factory=dict)
    target_file: Optional[str] = None
    target_files: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


class ShadowStatus(str, Enum):
    NOT_TRIGGERED = "NOT_TRIGGERED"
    OBSERVED = "OBSERVED"
    UNPROVEN = "UNPROVEN"
    PATCH_NOT_GENERATED = "PATCH_NOT_GENERATED"
    SANDBOX_REJECTED = "SANDBOX_REJECTED"
    SANDBOX_ACCEPTED = "SANDBOX_ACCEPTED"
    SHADOW_INTERNAL_ERROR = "SHADOW_INTERNAL_ERROR"
    DUPLICATE_EVENT = "DUPLICATE_EVENT"
    FROZEN_BASELINE_BLOCKED = "FROZEN_BASELINE_BLOCKED"


@dataclass
class ShadowRepairReport:
    shadow_run_id: str = field(default_factory=lambda: _new_id("shd"))
    mission_id: str = ""
    instance_id: Optional[str] = None
    incident_id: Optional[str] = None
    original_mission_status: str = ""
    failure_fingerprint: Optional[str] = None
    shadow_status: str = ShadowStatus.NOT_TRIGGERED.value
    shadow_mode: str = "SHADOW"
    shadow_triggered: bool = False
    trigger_reason: str = ""
    false_success_detected: bool = False
    root_cause: Optional[str] = None
    root_cause_proven: bool = False
    autonomous_patch_generated: bool = False
    candidate_repair: str = "SKIPPED"  # ACCEPT | REJECT | NOT_STARTED | SKIPPED | ERROR
    sandbox_executed: bool = False
    sandbox_manifest_dir: Optional[str] = None
    production_modified: bool = False
    production_hash_changed: bool = False
    auto_promotion: bool = False
    creator_approval_required: bool = True
    mission_intact: bool = True
    error_message: Optional[str] = None
    artifacts_persisted: List[str] = field(default_factory=list)
    timestamp: str = field(default_factory=_utc_iso)
    duplicate_shadow_event: bool = False
    new_repair_started: bool = False
    frozen_baseline_blocked: bool = False
    source_component: str = "unknown"
    attempt_number: int = 1
    max_attempts: int = 1
    guard_decision: str = "ALLOW"  # ALLOW | BLOCK
    guard_reason: str = ""
    recursive_source: Optional[str] = None
    circuit_state: str = "CLOSED"  # CLOSED | OPEN
    repair_attempt_blocked: bool = False
    self_repair_internal_failure: bool = False
    recursive_repair_started: bool = False
    provider_called: bool = False
    network_used: bool = False
    resume_decision: Optional[Dict[str, Any]] = None
    resume_result: Optional[Dict[str, Any]] = None
    mission_resumed: bool = False

    @property
    def MISSION_RESUMED(self) -> bool:
        return bool(self.mission_resumed)

    @property
    def FROZEN_BASELINE_BLOCKED(self) -> bool:
        return bool(self.frozen_baseline_blocked or self.shadow_status == ShadowStatus.FROZEN_BASELINE_BLOCKED.value)

    @property
    def DUPLICATE_SHADOW_EVENT(self) -> bool:
        return bool(self.duplicate_shadow_event or self.shadow_status == ShadowStatus.DUPLICATE_EVENT.value)

    @property
    def NEW_REPAIR_STARTED(self) -> bool:
        return bool(self.new_repair_started)

    @property
    def REPAIR_ATTEMPT_BLOCKED(self) -> bool:
        return bool(self.repair_attempt_blocked)

    @property
    def SELF_REPAIR_INTERNAL_FAILURE(self) -> bool:
        return bool(self.self_repair_internal_failure)

    @property
    def RECURSIVE_REPAIR_STARTED(self) -> bool:
        return bool(self.recursive_repair_started)

    @property
    def CIRCUIT_BREAKER(self) -> str:
        return self.circuit_state

    @property
    def CIRCUIT_STATE(self) -> str:
        return self.circuit_state

    @property
    def PROVIDER_CALLED(self) -> bool:
        return bool(self.provider_called)

    @property
    def NETWORK_USED(self) -> bool:
        return bool(self.network_used)

    @property
    def PRODUCTION_HASH_CHANGED(self) -> bool:
        return bool(self.production_hash_changed or self.production_modified)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


# ==============================================================================
# FASE 5A SCHEMAS: CONTROLLED AUTO-PROMOTION & ROLLBACK
# ==============================================================================

@dataclass
class PromotionDecision:
    promotion_allowed: bool = False
    decision: str = "BLOCK"  # ALLOW | BLOCK
    reason_code: str = ""
    target_file: str = ""
    target_is_non_critical: bool = False
    confidence: str = "UNPROVEN"
    sandbox_pass: bool = False
    focused_tests_pass: bool = False
    regression_tests_pass: bool = False
    rollback_ready: bool = False
    provider_called: bool = False
    network_used: bool = False
    target_is_critical: bool = False
    creator_approval_required: bool = True
    outside_workspace_blocked: bool = False
    root_cause_proven: bool = False
    details: Dict[str, Any] = field(default_factory=dict)

    @property
    def PROMOTION_ALLOWED(self) -> bool:
        return bool(self.promotion_allowed)

    @property
    def TARGET_IS_CRITICAL(self) -> bool:
        return bool(self.target_is_critical)

    @property
    def TARGET_IS_NON_CRITICAL(self) -> bool:
        return bool(self.target_is_non_critical)

    @property
    def CREATOR_APPROVAL_REQUIRED(self) -> bool:
        return bool(self.creator_approval_required)

    @property
    def OUTSIDE_WORKSPACE_BLOCKED(self) -> bool:
        return bool(self.outside_workspace_blocked)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "promotion_allowed": self.promotion_allowed,
            "decision": self.decision,
            "reason_code": self.reason_code,
            "target_file": self.target_file,
            "target_is_non_critical": self.target_is_non_critical,
            "confidence": self.confidence,
            "sandbox_pass": self.sandbox_pass,
            "focused_tests_pass": self.focused_tests_pass,
            "regression_tests_pass": self.regression_tests_pass,
            "rollback_ready": self.rollback_ready,
            "provider_called": self.provider_called,
            "network_used": self.network_used,
            "target_is_critical": self.target_is_critical,
            "creator_approval_required": self.creator_approval_required,
            "outside_workspace_blocked": self.outside_workspace_blocked,
            "root_cause_proven": self.root_cause_proven,
            "details": dict(self.details),
        }


@dataclass
class RollbackArtifact:
    promotion_id: str
    target_path: str
    original_hash: str
    candidate_hash: str
    original_bytes_snapshot: bytes = b""
    promotion_timestamp: str = field(default_factory=_utc_iso)
    rollback_ready: bool = True
    rollback_triggered: bool = False
    rollback_completed: bool = False
    production_restored: bool = False
    original_hash_restored: bool = False
    restored_hash: Optional[str] = None
    error: Optional[str] = None

    @property
    def ROLLBACK_READY(self) -> bool:
        return bool(self.rollback_ready)

    @property
    def ROLLBACK_TRIGGERED(self) -> bool:
        return bool(self.rollback_triggered)

    @property
    def ROLLBACK_COMPLETED(self) -> bool:
        return bool(self.rollback_completed)

    @property
    def PRODUCTION_RESTORED(self) -> bool:
        return bool(self.production_restored)

    @property
    def ORIGINAL_HASH_RESTORED(self) -> bool:
        return bool(self.original_hash_restored)

    def to_dict(self) -> Dict[str, Any]:
        d = asdict(self)
        if "original_bytes_snapshot" in d:
            d["original_bytes_len"] = len(self.original_bytes_snapshot)
            del d["original_bytes_snapshot"]
        return d


@dataclass
class PromotionExecutionResult:
    promotion_id: str
    target_file: str
    decision: Optional[PromotionDecision] = None
    auto_promotion: bool = False
    promotion_verified: bool = False
    production_file_changed: bool = False
    rollback_ready: bool = False
    rollback_triggered: bool = False
    rollback_completed: bool = False
    production_restored: bool = False
    original_hash_restored: bool = False
    repair_applied_and_verified: bool = False
    mission_continuation_eligible: bool = False
    initial_hash: str = ""
    promoted_hash: Optional[str] = None
    final_hash: Optional[str] = None
    error_message: Optional[str] = None
    rollback_artifact: Optional[RollbackArtifact] = None
    timestamp: str = field(default_factory=_utc_iso)

    @property
    def PROMOTION_ALLOWED(self) -> bool:
        return bool(self.decision.promotion_allowed if self.decision else False)

    @property
    def AUTO_PROMOTION(self) -> bool:
        return bool(self.auto_promotion)

    @property
    def PROMOTION_VERIFIED(self) -> bool:
        return bool(self.promotion_verified)

    @property
    def PRODUCTION_FILE_CHANGED(self) -> bool:
        return bool(self.production_file_changed)

    @property
    def ROLLBACK_READY(self) -> bool:
        return bool(self.rollback_ready)

    @property
    def ROLLBACK_TRIGGERED(self) -> bool:
        return bool(self.rollback_triggered)

    @property
    def ROLLBACK_COMPLETED(self) -> bool:
        return bool(self.rollback_completed)

    @property
    def PRODUCTION_RESTORED(self) -> bool:
        return bool(self.production_restored)

    @property
    def ORIGINAL_HASH_RESTORED(self) -> bool:
        return bool(self.original_hash_restored)

    @property
    def REPAIR_APPLIED_AND_VERIFIED(self) -> bool:
        return bool(self.repair_applied_and_verified)

    @property
    def MISSION_CONTINUATION_ELIGIBLE(self) -> bool:
        return bool(self.mission_continuation_eligible)

    def to_dict(self) -> Dict[str, Any]:
        d = asdict(self)
        if self.decision:
            d["decision"] = self.decision.to_dict()
        if self.rollback_artifact:
            d["rollback_artifact"] = self.rollback_artifact.to_dict()
        return d


# ==============================================================================
# FASE 5B SCHEMAS: CONTROLLED MISSION RESUME & CONTINUITY
# ==============================================================================

@dataclass
class ResumeDecision:
    resume_allowed: bool
    mission_id: str
    instance_id: str
    failure_fingerprint: str
    repair_verified: bool
    promotion_verified: bool
    attempt_number: int = 1
    resume_count: int = 0
    reason_code: str = ""
    decision: str = "BLOCK"  # ALLOW | BLOCK
    mission_id_match: bool = True
    task_id_match: bool = True
    client_task_id_match: bool = True
    instance_id_match: bool = True
    workspace_root_match: bool = True
    conversation_id_match: bool = True
    identity_preserved: bool = True
    details: Dict[str, Any] = field(default_factory=dict)
    timestamp: str = field(default_factory=_utc_iso)

    @property
    def RESUME_ALLOWED(self) -> bool:
        return bool(self.resume_allowed)

    @property
    def MISSION_ID_MATCH(self) -> bool:
        return bool(self.mission_id_match)

    @property
    def TASK_ID_MATCH(self) -> bool:
        return bool(self.task_id_match)

    @property
    def CLIENT_TASK_ID_MATCH(self) -> bool:
        return bool(self.client_task_id_match)

    @property
    def INSTANCE_ID_MATCH(self) -> bool:
        return bool(self.instance_id_match)

    @property
    def WORKSPACE_ROOT_MATCH(self) -> bool:
        return bool(self.workspace_root_match)

    @property
    def CONVERSATION_ID_MATCH(self) -> bool:
        return bool(self.conversation_id_match)

    @property
    def IDENTITY_PRESERVED(self) -> bool:
        return bool(self.identity_preserved)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


@dataclass
class MissionResumeResult:
    mission_id: str
    task_id: str
    instance_id: str
    workspace_root: str
    client_task_id: Optional[str] = None
    conversation_id: Optional[str] = None
    decision: Optional[ResumeDecision] = None
    mission_resumed: bool = False
    identity_preserved: bool = True
    final_verification: str = "NOT_RUN"  # PASS | FAIL | NOT_RUN
    final_mission_state: str = "FAILED"  # COMPLETED_AND_E2E_VERIFIED | FAILED | ABORTED
    evidence: Dict[str, Any] = field(default_factory=dict)
    second_autorepair_blocked: bool = False
    mission_requires_creator_review: bool = False
    duplicate_resume: bool = False
    error_message: Optional[str] = None
    timestamp: str = field(default_factory=_utc_iso)

    @property
    def MISSION_RESUME_ALLOWED(self) -> bool:
        return bool(self.decision.resume_allowed if self.decision else False)

    @property
    def MISSION_RESUMED(self) -> bool:
        return bool(self.mission_resumed)

    @property
    def IDENTITY_PRESERVED(self) -> bool:
        return bool(self.identity_preserved)

    @property
    def MISSION_ID_MATCH(self) -> bool:
        return bool(self.decision.mission_id_match if self.decision else True)

    @property
    def TASK_ID_MATCH(self) -> bool:
        return bool(self.decision.task_id_match if self.decision else True)

    @property
    def CLIENT_TASK_ID_MATCH(self) -> bool:
        return bool(self.decision.client_task_id_match if self.decision else True)

    @property
    def INSTANCE_ID_MATCH(self) -> bool:
        return bool(self.decision.instance_id_match if self.decision else True)

    @property
    def WORKSPACE_ROOT_MATCH(self) -> bool:
        return bool(self.decision.workspace_root_match if self.decision else True)

    @property
    def CONVERSATION_ID_MATCH(self) -> bool:
        return bool(self.decision.conversation_id_match if self.decision else True)

    @property
    def FINAL_VERIFICATION(self) -> str:
        return self.final_verification

    @property
    def FINAL_MISSION_STATE(self) -> str:
        return self.final_mission_state

    @property
    def SECOND_AUTOREPAIR_BLOCKED(self) -> bool:
        return bool(self.second_autorepair_blocked)

    @property
    def MISSION_REQUIRES_CREATOR_REVIEW(self) -> bool:
        return bool(self.mission_requires_creator_review)

    @property
    def DUPLICATE_RESUME(self) -> bool:
        return bool(self.duplicate_resume)

    def to_dict(self) -> Dict[str, Any]:
        d = asdict(self)
        if self.decision:
            d["decision"] = self.decision.to_dict()
        return d






