"""Regression tests for the opus truncated-capture bug (root-caused 2026-06-06).

The opus engine captures `claude --print` stdout verbatim. When the model role-plays
"I wrote the deliverable to <file>" and emits only a short sign-off, that sign-off was
saved as the whole artifact. Two defenses, both tested here:
  - _build_opus_user_content injects an OUTPUT CONTRACT forbidding the role-play.
  - _is_degenerate_response detects the sign-off signature the length guard misses.
"""

import importlib.util
from pathlib import Path

_CONSULT = Path(__file__).resolve().parents[1] / "consult.py"
_spec = importlib.util.spec_from_file_location("consult_under_test", _CONSULT)
consult = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(consult)


# --- The detector: real-world failure captures must be flagged -------------------

# Verbatim shape of the 1064-char bitcoin failure (passed the 200/400 length guards).
BITCOIN_SIGNOFF = (
    "Opus R2 written to `/consultations/personal/bitcoin-mss-swing-yubit/"
    "opus_round_2.md`. Key moves this round:\n"
    "- Downgraded the MSS edge to a coin-flip after stress-testing the basis.\n"
    "- Caught Gemini's hidden assumption that funding stays positive.\n"
    "- Reframed the sizing around liquidation distance, not conviction.\n"
) + ("- Additional bullet of plausible-looking but non-deliverable commentary.\n" * 6)

# The studio-heartbeat first-attempt failure shape.
STUDIO_SIGNOFF = (
    "The Opus second-lab take is complete at `opus_round_1.md`. Net deliverable: I "
    "confirmed the locked design holds, contributed one blocking catch, and three "
    "divergences. The full diff-from-Codex is in the 'Net delta' section at the bottom "
    "of the file for whoever runs the synthesis round."
)


def test_bitcoin_signoff_flagged():
    assert consult._is_degenerate_response(BITCOIN_SIGNOFF) is True


def test_studio_signoff_flagged():
    assert consult._is_degenerate_response(STUDIO_SIGNOFF) is True


def test_near_empty_flagged():
    assert consult._is_degenerate_response("ok done") is True


# --- No false positives: legitimate deliverables must pass ----------------------

def test_long_real_deliverable_not_flagged():
    # A real deliverable is long; mentioning a file in prose must not trip the guard.
    body = (
        "# Round 1 — Design\n\n## 1. Build vs adopt\n"
        "We recommend thin glue. The harness already writes its log to a file, and "
        "the lease is saved to disk; none of that changes the recommendation.\n\n"
    ) + ("Substantive analysis paragraph carrying real design content. " * 80)
    assert len(body) > 2000
    assert consult._is_degenerate_response(body) is False


def test_short_deliverable_mentioning_a_design_file_not_flagged():
    # Design prose that mentions a non-output file path must not trip the detector,
    # even when short. ".session-lease.json" is not an output artifact.
    body = (
        "# Round 2 — Lease design\n\n"
        "The global lease is saved to `.session-lease.json` on local disk, acquired "
        "via shlock. The build lease is written to a separate per-worktree path so the "
        "two never collide.\n\n"
    ) + ("Concrete rationale about TTL and stale reclamation behavior. " * 12)
    assert len(body) < 2000
    assert consult._is_degenerate_response(body) is False


def test_short_legit_convergence_not_flagged():
    # A genuinely short final-convergence round (~1.5k chars) with no file role-play.
    body = (
        "# Round 3 — Final Convergence\n\n"
        "## Decision A: lease TTL = 15m. Both engines agree.\n"
        "## Decision B: spend cap enforced before each paid unit.\n"
        "## Decision C: never auto-merge; PR is terminal.\n\n"
    ) + ("Rationale sentence locking the decision with concrete reasoning. " * 18)
    assert len(body) < 2000
    assert consult._is_degenerate_response(body) is False


# --- The prevention contract is injected for opus paths -------------------------

def test_opus_user_content_has_output_contract():
    content = consult._build_opus_user_content(
        prompt="Design the thing.", context="ctx", prior_context=None, follow_up=None
    )
    assert "OUTPUT CONTRACT" in content
    assert "Your message IS the" in content
    # The actual prompt must still be present, after the contract.
    assert "Design the thing." in content
    assert content.index("OUTPUT CONTRACT") < content.index("Design the thing.")
