# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-to-compact-the-summarization-floor-invariant-in-long-horizon-agent-transcripts.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-to-compact-the-summarization-floor-invariant-in-long-horizon-agent-transcripts.warrant.json
# Markdown SHA-256:   9ac0006447bcc8144191d4f17c2796e7291bee9509a5ed3ec360a4050874b5e5
# Code AST SHA-256:   3554041d004cb51c583b75f5eb6307af841fb9fce0a28e4884201db791decead
# Verified Classes:   CompactionFloorError, FloorEstimate
# Defensive Guards:   3 fail-closed invariants (zero external deps)

"""Compaction floor guard: refuse to compact a segment into a slot that
cannot hold its irreducible tokens. Enforces the invariant from the record."""

import re
from dataclasses import dataclass


class CompactionFloorError(RuntimeError):
    """Raised when a summary slot is smaller than the segment's irreducible floor."""


# Tokens that cannot be paraphrased without changing meaning: quoted strings,
# numeric literals, identifiers with dots/slashes/colons, and constraint verbs.
_IRREDUCIBLE_PATTERNS = [
    re.compile(r'"[^"]*"|\'[^\']*\''),          # quoted strings / regexes
    re.compile(r'\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b'),  # numbers, versions, errnos
    re.compile(r'\b[\w.-]+/[\w./-]+\b'),        # paths
    re.compile(r'\b[A-Z_]{2,}\b'),              # constants / env vars
]
_CONSTRAINT_CUES = ("must", "never", "always", "exactly", "only", "do not")


@dataclass
class FloorEstimate:
    irreducible_tokens: int
    margin_tokens: int

    @property
    def floor(self) -> int:
        return self.irreducible_tokens + self.margin_tokens


def estimate_floor(segment: str, margin_ratio: float = 0.20) -> FloorEstimate:
    """Cheap pre-pass: sum tokens of irreducible spans plus a coherence margin."""
    seen = set()
    irreducible = 0
    for pat in _IRREDUCIBLE_PATTERNS:
        for m in pat.finditer(segment):
            span = m.group(0)
            if span not in seen:
                seen.add(span)
                irreducible += max(1, len(span) // 4)  # ~4 chars/token
    lowered = segment.lower()
    for cue in _CONSTRAINT_CUES:
        if cue in lowered:
            irreducible += 8  # cost of preserving the constraint sentence
    margin = int(irreducible * margin_ratio)
    return FloorEstimate(irreducible_tokens=irreducible, margin_tokens=margin)


def compact(segment: str, slot_size: int, summarize) -> str:
    """Compact only if the slot clears the floor; otherwise refuse loudly."""
    est = estimate_floor(segment)
    if slot_size < est.floor:
        raise CompactionFloorError(
            f"slot_size={slot_size} < floor={est.floor} "
            f"(irreducible={est.irreducible_tokens}, margin={est.margin_tokens}); "
            f"refuse, split, or raise budget — do not shorten the summary"
        )
    summary = summarize(segment, max_tokens=slot_size)
    if not summary or len(summary) // 4 > slot_size:
        raise CompactionFloorError(
            f"summarizer returned {len(summary)//4} tokens for slot {slot_size}"
        )
    return summary


def compact_or_split(segment: str, slot_size: int, summarize, split_at: int):
    """Split when the whole segment is infeasible but halves are not."""
    est = estimate_floor(segment)
    if slot_size >= est.floor:
        return [compact(segment, slot_size, summarize)]
    left, right = segment[:split_at], segment[split_at:]
    half_slot = slot_size // 2
    if estimate_floor(left).floor <= half_slot and estimate_floor(right).floor <= half_slot:
        return [compact(left, half_slot, summarize), compact(right, half_slot, summarize)]
    raise CompactionFloorError(
        f"segment infeasible at slot {slot_size} and unsplittable at {split_at}; escalate"
    )
