from __future__ import annotations

import json
import os
import subprocess
import sys
from pathlib import Path

import pytest

from aictx import cli
from aictx.adapters import install_global_adapters
from aictx.agent_runtime import render_agent_runtime, render_repo_agents_block
from aictx.continuity import DECISIONS_PATH, HANDOFF_PATH, LAST_EXECUTION_SUMMARY_PATH, RESUME_CAPSULE_JSON_PATH, RESUME_CAPSULE_MARKDOWN_PATH
from aictx.middleware import finalize_execution, prepare_execution
from aictx.repo_map.config import write_repomap_config, write_repomap_index
from aictx.scaffold import init_repo_scaffold
from aictx.state import REPO_FAILURE_MEMORY_DIR, REPO_STRATEGY_MEMORY_DIR, write_json
from aictx.work_state import close_work_state, load_work_state, start_work_state


def _parser():
    return cli.build_parser()


def _seed_repomap(repo: Path) -> None:
    (repo / "src/aictx").mkdir(parents=True)
    (repo / "src/aictx/continuity.py").write_text("def build_resume_capsule():\n    pass\n", encoding="utf-8")
    (repo / "tests").mkdir()
    (repo / "tests/test_resume_command.py").write_text("def test_resume():\n    pass\n", encoding="utf-8")
    write_repomap_config(repo, {"enabled": True})
    write_repomap_index(
        repo,
        {
            "version": 1,
            "provider": "tree_sitter",
            "mode": "full",
            "files": [
                {
                    "path": "src/aictx/continuity.py",
                    "language": "python",
                    "symbols": [{"name": "build_resume_capsule", "kind": "function", "line": 1, "language": "python"}],
                    "imports": [],
                    "metadata_only": False,
                    "provider": "tree_sitter",
                    "reason": "",
                    "size_bytes": 10,
                },
                {
                    "path": "tests/test_resume_command.py",
                    "language": "python",
                    "symbols": [{"name": "test_resume", "kind": "function", "line": 1, "language": "python"}],
                    "imports": [],
                    "metadata_only": False,
                    "provider": "tree_sitter",
                    "reason": "",
                    "size_bytes": 10,
                },
            ],
        },
    )


def _seed_parser_fixture(repo: Path) -> None:
    (repo / "src/taskflow").mkdir(parents=True)
    (repo / "src/taskflow/parser.py").write_text("def parse_blocked():\n    pass\n", encoding="utf-8")
    (repo / "tests").mkdir(exist_ok=True)
    (repo / "tests/test_parser.py").write_text("def test_blocked_edge_cases():\n    pass\n", encoding="utf-8")
    (repo / "README.md").write_text("# Quickstart\n", encoding="utf-8")
    write_repomap_config(repo, {"enabled": True})
    write_repomap_index(
        repo,
        {
            "version": 1,
            "files": [
                {
                    "path": "README.md",
                    "language": "markdown",
                    "symbols": [{"name": "Quickstart", "kind": "heading", "line": 1, "language": "markdown"}],
                },
                {
                    "path": "src/taskflow/parser.py",
                    "language": "python",
                    "symbols": [{"name": "parse_blocked", "kind": "function", "line": 1, "language": "python"}],
                },
                {
                    "path": "tests/test_parser.py",
                    "language": "python",
                    "symbols": [{"name": "test_blocked_edge_cases", "kind": "function", "line": 1, "language": "python"}],
                },
            ],
        },
    )


def _seed_generic_repomap(repo: Path, files: dict[str, tuple[str, str]]) -> None:
    for rel_path, (language, symbol) in files.items():
        target = repo / rel_path
        target.parent.mkdir(parents=True, exist_ok=True)
        if language == "markdown":
            target.write_text(f"# {symbol}\n", encoding="utf-8")
        elif language in {"toml", "yaml"}:
            target.write_text(f"# {symbol}\n", encoding="utf-8")
        else:
            target.write_text(f"def {symbol}():\n    pass\n", encoding="utf-8")
    write_repomap_config(repo, {"enabled": True})
    write_repomap_index(
        repo,
        {
            "version": 1,
            "files": [
                {
                    "path": rel_path,
                    "language": language,
                    "symbols": [
                        {
                            "name": symbol,
                            "kind": "heading" if language == "markdown" else "function",
                            "line": 1,
                            "language": language,
                        }
                    ],
                }
                for rel_path, (language, symbol) in files.items()
            ],
        },
    )


def _write_jsonl(path: Path, rows: list[dict]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("\n".join(json.dumps(row) for row in rows) + "\n", encoding="utf-8")


def _arbiter_script(path: Path) -> None:
    path.write_text(
        "\n".join(
            [
                "import json, sys",
                "payload = json.loads(sys.stdin.read() or '{}')",
                "request = str(payload.get('task', {}).get('request_text') or '').lower()",
                "candidate = str(payload.get('candidate', {}).get('path') or '')",
                "if 'continue previous task' in request and candidate.endswith('parser.py'):",
                "    out = {'relation': 'continuation', 'confidence': 0.93, 'recommended_priority': 'keep', 'reason_short': 'same parser thread'}",
                "elif 'entry point ranking' in request and candidate.endswith('github-release.yml'):",
                "    out = {'relation': 'unrelated', 'confidence': 0.91, 'recommended_priority': 'ignore', 'reason_short': 'release flow is not current task'}",
                "else:",
                "    out = {'relation': 'adjacent', 'confidence': 0.61, 'recommended_priority': 'demote', 'reason_short': 'adjacent but not primary'}",
                "print(json.dumps(out))",
            ]
        )
        + "\n",
        encoding="utf-8",
    )


def _write_python_script(path: Path, source: str) -> None:
    path.write_text(source.strip() + "\n", encoding="utf-8")


def test_resume_default_markdown_and_budget(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "implement resume capsule"])
    assert args.func(args) == 0

    output = capsys.readouterr().out
    assert output.startswith("AICTX continuity capsule\n")
    assert "Startup banner to render" in output
    assert "Startup rule" in output
    assert "This capsule is the operational brief" in output
    assert "Do not read `.aictx/agent_runtime.md`" in output
    assert "Do not inspect `.aictx/**`" in output
    assert "Do not inspect local/global AICTX installation files" in output
    assert "Run no further AICTX discovery commands" in output
    assert "aictx finalize --repo ." in output
    assert "--status success|failure" in output
    assert "--summary" in output
    assert "First action" in output
    assert "startup_banner_policy.show_in_first_user_visible_response" not in output
    assert "Current request" in output
    assert "Avoid" in output
    assert len(output) <= 6000
    assert (repo / RESUME_CAPSULE_MARKDOWN_PATH).exists()
    assert (repo / RESUME_CAPSULE_JSON_PATH).exists()


def test_resume_infers_codex_identity_from_environment(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    monkeypatch.setenv("CODEX_THREAD_ID", "thread-from-env")
    for key in ("CLAUDE_SESSION_ID", "CLAUDE_CONVERSATION_ID", "CLAUDE_THREAD_ID", "CLAUDE_CODE_SESSION_ID"):
        monkeypatch.delenv(key, raising=False)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "identity", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["startup_banner_render_payload"]["header"]["agent_label"].startswith("codex@")
    assert payload["startup_banner_text"].startswith("codex@")


def test_resume_policy_requires_localized_substantive_banner(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "policy", "--json", "--agent-id", "codex"])
    assert args.func(args) == 0

    policy = json.loads(capsys.readouterr().out)["startup_banner_policy"]
    assert policy["render_payload_field"] == "startup_banner_render_payload"
    assert "current user language" in policy["instruction"]
    assert "first substantive user-visible response" in policy["instruction"]
    assert "transient progress/status message" in policy["instruction"]


def test_resume_json_schema_and_written_files(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "resume command", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["schema_version"] == "1.0"
    assert payload["mode"] == "agent_brief"
    assert payload["request"] == "resume command"
    assert payload["startup_banner_text"]
    assert payload["startup_banner_policy"]["source"] == "resume"
    assert payload["startup_banner_policy"]["data_source"] == "load_continuity_context"
    assert payload["startup_banner_policy"]["does_not_replace_prepare_execution"] is True
    assert payload["sources"]["startup_banner"] == "load_continuity_context.startup_banner_text"
    assert payload["sources"]["final_summary"] == "finalize_execution.agent_summary_text"
    assert payload["startup_guard"]["resume_is_self_contained"] is True
    assert payload["startup_guard"]["do_not_read_runtime_files"] is True
    assert payload["startup_guard"]["do_not_inspect_aictx_installation"] is True
    assert payload["startup_guard"]["allowed_aictx_commands_before_first_task_action"] == ["resume"]
    assert payload["startup_guard"]["allowed_aictx_commands_after_task_action"] == ["finalize"]
    assert payload["previous_contract_result"]["status"] == "unknown"
    assert "aictx internal execution finalize" in payload["startup_guard"]["forbidden_normal_flow"]
    assert "direct shell calls to finalize_execution" in payload["startup_guard"]["forbidden_normal_flow"]
    assert ".aictx/agent_runtime.md" in payload["startup_guard"]["forbidden_before_first_task_action"]
    assert "local/global AICTX installation files" in payload["startup_guard"]["forbidden_before_first_task_action"]
    assert payload["capsule"]["first_action"]["type"] in {"open_file", "inspect_entry_points", "follow_current_request", "ask_clarification"}
    assert payload["task_state"]["status"] in {"active", "completed", "blocked", "unknown"}
    assert payload["written_files"] == {
        "markdown": ".aictx/continuity/resume_capsule.md",
        "json": ".aictx/continuity/resume_capsule.json",
    }
    assert json.loads((repo / RESUME_CAPSULE_JSON_PATH).read_text(encoding="utf-8"))["schema_version"] == "1.0"


def test_resume_prioritizes_contract_gap_carryover_over_completed_handoff(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    (repo / "tests").mkdir()
    (repo / "tests/test_parser.py").write_text("def test_parser():\n    pass\n", encoding="utf-8")
    prepared = prepare_execution({
        "repo_root": str(repo),
        "user_request": "fix parser",
        "agent_id": "codex",
        "adapter_id": "codex",
        "execution_id": "exec-carryover-e2e",
        "timestamp": "2026-05-13T10:00:00Z",
        "files_opened": ["tests/test_parser.py"],
    })
    prepared["resume_contract"] = {
        "execution_contract": {
            "task_goal": "fix parser",
            "first_action": {"path": "tests/test_parser.py", "binding": "must_open_first"},
            "edit_scope": {"primary": ["tests/test_parser.py"], "secondary_if_needed": []},
            "test_command": {"command": "pytest -q tests/test_parser.py"},
        },
        "task_goal": "fix parser",
    }

    finalize_execution(prepared, {"success": True, "result_summary": "updated parser without running canonical validation"})
    state = load_work_state(repo, "contract-carryover-fix-parser")
    assert state["status"] == "paused"
    assert any("needs-validation" in item and "Canonical test command was not observed." in item for item in state["unverified"])

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix parser", "--json"])
    assert args.func(args) == 0
    payload = json.loads(capsys.readouterr().out)

    loaded = payload["loaded_context"]
    assert loaded[0]["kind"] == "work_state"
    assert loaded[0]["role"] == "carryover"
    assert loaded[0]["selection_reason"] == "unresolved Work State carryover before completed handoff"
    assert "run expected validation command: pytest -q tests/test_parser.py" in payload["capsule"]["next_action"]
    handoff_indexes = [index for index, item in enumerate(loaded) if item["kind"] == "handoff"]
    assert handoff_indexes
    assert handoff_indexes[0] > 0


def test_resume_includes_compact_previous_contract_line_once(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    metrics = repo / ".aictx" / "metrics" / "contract_compliance.jsonl"
    metrics.parent.mkdir(parents=True, exist_ok=True)
    metrics.write_text(
        json.dumps({
            "status": "partial",
            "score": 0.9,
            "main_issue": "canonical_test_not_observed",
            "compact_summary": "Contract: partial — canonical_test_not_observed.",
        }) + "\n",
        encoding="utf-8",
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix parser", "--json"])
    assert args.func(args) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["previous_contract_result"]["status"] == "partial"

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix parser"])
    assert args.func(args) == 0
    output = capsys.readouterr().out
    assert output.count("Previous contract:") == 1
    assert "Previous contract: partial — canonical_test_not_observed." in output
    assert len(output) <= 6000

def test_resume_rejects_legacy_request_flag(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    with pytest.raises(SystemExit):
        _parser().parse_args(["resume", "--repo", str(repo), "--request", "legacy request"] )

def test_runtime_instructions_require_task_goal_only():
    for text in (render_agent_runtime(), render_repo_agents_block()):
        assert 'aictx resume --repo . --task "<task goal>" --json' in text
        assert "Do not pass the full user prompt to resume" in text
        assert "reporting instructions" in text
        assert "metrics schemas" in text
        assert "output format rules" in text
        assert "aictx finalize --repo ." in text
        assert "final AICTX summary" in text
        assert "continuity_view_online" in text
        assert "Mermaid" in text
        assert "replace" in text


def test_resume_json_stdout_is_valid_for_json_tool(tmp_path: Path):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    env = os.environ.copy()
    env["PYTHONPATH"] = str(Path(__file__).resolve().parents[1] / "src")

    resume = subprocess.run(
        [
            sys.executable,
            "-m",
            "aictx",
            "resume",
            "--repo",
            str(repo),
            "--task",
            "json pipe check",
            "--json",
        ],
        text=True,
        capture_output=True,
        env=env,
        check=True,
    )
    formatted = subprocess.run(
        [sys.executable, "-m", "json.tool"],
        input=resume.stdout,
        text=True,
        capture_output=True,
        check=True,
    )
    assert '"schema_version": "1.0"' in formatted.stdout


def test_resume_full_has_more_detail_than_default(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    (repo / DECISIONS_PATH).write_text(
        "\n".join(json.dumps({"decision": f"Decision {i}", "related_paths": []}) for i in range(6)) + "\n",
        encoding="utf-8",
    )

    parser = _parser()
    assert parser.parse_args(["resume", "--repo", str(repo), "--task", "decision", "--json"]).func(
        parser.parse_args(["resume", "--repo", str(repo), "--task", "decision", "--json"])
    ) == 0
    compact = json.loads(capsys.readouterr().out)
    assert parser.parse_args(["resume", "--repo", str(repo), "--task", "decision", "--json", "--full"]).func(
        parser.parse_args(["resume", "--repo", str(repo), "--task", "decision", "--json", "--full"])
    ) == 0
    full = json.loads(capsys.readouterr().out)

    assert len(full["capsule"]["decisions"]) > len(compact["capsule"]["decisions"])


def test_resume_active_work_state_drives_task_state(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    start_work_state(
        repo,
        "Implement resume command",
        initial={"next_action": "inspect src/aictx/continuity.py", "active_files": ["src/aictx/continuity.py"]},
    )
    (repo / "src/aictx").mkdir(parents=True)
    (repo / "src/aictx/continuity.py").write_text("", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "resume", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["task_state"]["status"] == "active"
    assert payload["task_state"]["confidence"] == "high"
    assert payload["continuity_match"]["level"] == "high"
    assert payload["execution_contract"]["contract_strength"] == "strict"
    assert payload["capsule"]["next_action"] == "inspect src/aictx/continuity.py"
    assert payload["capsule"]["first_action"]["path"] == "src/aictx/continuity.py"
    assert payload["loaded_context"][0]["kind"] == "work_state"
    assert payload["loaded_context"][0]["role"] == "primary"


def test_resume_first_action_prefers_tests_for_implementation_task(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate BLOCKED parser edge cases", "--json"])
    assert args.func(args) == 0

    first_action = json.loads(capsys.readouterr().out)["capsule"]["first_action"]
    assert first_action["type"] == "open_file"
    assert first_action["path"] == "tests/test_parser.py"


def test_resume_json_includes_closed_loop_execution_contract(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    (repo / "Makefile").write_text("test:\n\tpytest -q\n", encoding="utf-8")
    (repo / "examples").mkdir()
    (repo / "examples/tasks.txt").write_text("demo", encoding="utf-8")
    (repo / "pyproject.toml").write_text("[tool.pytest.ini_options]\n", encoding="utf-8")

    args = _parser().parse_args([
        "resume",
        "--repo",
        str(repo),
        "--task",
        "Continue previous task: handle BLOCKED parser edge cases",
        "--json",
    ])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    contract = payload["execution_contract"]
    assert contract["mode"] == "closed_loop"
    assert contract["task_goal"] == "Continue previous task: handle BLOCKED parser edge cases"
    assert payload["continuity_match"]["level"] == "high"
    assert contract["continuity_match"]["level"] == "high"
    assert contract["contract_strength"] == "strict"
    assert contract["first_action"]["binding"] == "must_open_first"
    assert contract["first_action_policy"]["must_follow"] is True
    assert contract["test_command"]["command"] == "make test"
    for forbidden in [
        "git status for orientation",
        "git diff for orientation",
        "ls",
        "find",
        "repo-wide grep",
        "read README.md",
        "read docs/**",
        "read examples/**",
        "manual Python probes",
        "test command before first edit",
        "alternative test command discovery",
        "inspect .aictx/**",
        "inspect local/global AICTX installation files",
    ]:
        assert forbidden in contract["forbidden_before_first_edit"]
    assert "tests/test_parser.py" in contract["edit_scope"]["primary"]
    assert "src/taskflow/parser.py" in contract["edit_scope"]["secondary_if_needed"]
    assert "README.md" in contract["edit_scope"]["avoid"]
    assert "aictx finalize" in contract["finalize_command"]


def test_resume_first_action_text_precedes_source_index(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases"])
    assert args.func(args) == 0

    output = capsys.readouterr().out
    assert output.index("First action") < output.index("Source index")


def test_resume_execution_contract_text_precedes_source_index(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases"])
    assert args.func(args) == 0

    output = capsys.readouterr().out
    assert output.index("Execution contract") < output.index("Source index")
    assert output.index("Continuity match") < output.index("Source index")
    assert "Contract strength" in output
    assert "strict=binding, soft=guided with fallback, exploratory=guardrails" in output
    assert "git status for safety" in output
    for phrase in [
        "Open first action",
        "Do not perform repo-wide orientation before first edit",
        "Validate with",
        "Finalize with",
    ]:
        assert phrase in output


def test_resume_contract_selects_makefile_then_pytest_fallback(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    (repo / "Makefile").write_text("test:\n\tpytest -q\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0
    assert json.loads(capsys.readouterr().out)["execution_contract"]["test_command"]["command"] == "make test"

    repo2 = tmp_path / "repo2"
    init_repo_scaffold(repo2, update_gitignore=False)
    _seed_parser_fixture(repo2)
    (repo2 / "pyproject.toml").write_text("[tool.pytest.ini_options]\n", encoding="utf-8")
    args = _parser().parse_args(["resume", "--repo", str(repo2), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0
    assert "pytest" in json.loads(capsys.readouterr().out)["execution_contract"]["test_command"]["command"]


def test_resume_implementation_task_does_not_choose_readme_first(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    write_json(repo / HANDOFF_PATH, {"summary": "readme stale", "recommended_starting_points": ["README.md", "src/taskflow/parser.py"]})

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0

    assert json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"] != "README.md"


def test_resume_docs_task_can_choose_readme(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    write_json(repo / HANDOFF_PATH, {"summary": "docs", "recommended_starting_points": ["src/taskflow/parser.py", "README.md"]})

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "update README quickstart documentation", "--json"])
    assert args.func(args) == 0

    assert json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"] == "README.md"


def test_resume_generic_bugfix_prefers_source_or_tests_over_readme(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "README.md": ("markdown", "Payment validation docs"),
            "src/payments/validation.py": ("python", "validate_payment"),
            "tests/test_payment_validation.py": ("python", "test_payment_validation_bug"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix payment validation bug", "--json"])
    assert args.func(args) == 0

    first_path = json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"]
    assert first_path in {"tests/test_payment_validation.py", "src/payments/validation.py"}
    assert first_path != "README.md"


def test_resume_relevant_repomap_without_work_state_is_medium_soft(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "src/payments/validation.py": ("python", "validate_payment"),
            "tests/test_payment_validation.py": ("python", "test_payment_validation_bug"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix payment validation bug", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["continuity_match"]["level"] == "medium"
    assert payload["execution_contract"]["contract_strength"] == "soft"
    assert payload["execution_contract"]["first_action"]["binding"] == "must_open_first"


def test_resume_unrelated_completed_handoff_is_low_exploratory(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    (repo / "src/taskflow").mkdir(parents=True)
    (repo / "src/taskflow/parser.py").write_text("def parse_task():\n    pass\n", encoding="utf-8")
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished parser work.",
            "recommended_starting_points": ["src/taskflow/parser.py"],
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "update button colors", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    contract = payload["execution_contract"]
    assert payload["continuity_match"]["level"] == "low"
    assert contract["contract_strength"] == "exploratory"
    assert contract["first_action"]["type"] == "inspect_entry_points"
    assert contract["first_action"]["path"] == ""
    assert contract["first_action"]["binding"] == "must_inspect_listed_entry_points_only"
    assert contract["first_action_policy"]["must_follow"] is False
    assert contract["edit_scope"]["primary"] == []
    assert "src/taskflow/parser.py" not in contract["edit_scope"]["primary"]
    assert "inspect .aictx/**" in contract["forbidden_before_first_edit"]
    assert any(item["path"] == "src/taskflow/parser.py" for item in payload["capsule"]["fallback_entry_points"])
    assert "Background continuity:" in payload["startup_banner_text"]
    assert "Resuming: Finished parser work." not in payload["startup_banner_text"]


def test_resume_completed_handoff_is_demoted_when_repomap_is_more_relevant(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "src/aictx/continuity/__init__.py": ("python", "build_resume_capsule"),
            "tests/test_resume_command.py": ("python", "test_resume_entry_points"),
        },
    )
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    first_path = payload["capsule"]["first_action"]["path"]
    assert first_path in {"src/aictx/continuity/__init__.py", "tests/test_resume_command.py"}
    assert payload["capsule"]["entry_points"][0]["path"] != ".github/workflows/github-release.yml"
    assert any(item["path"] == ".github/workflows/github-release.yml" for item in payload["capsule"]["fallback_entry_points"])
    handoff_fallback = next(item for item in payload["capsule"]["fallback_entry_points"] if item["path"] == ".github/workflows/github-release.yml")
    assert "low relevance" in handoff_fallback["reason"]


def test_resume_active_work_state_still_wins_over_unrelated_request_text(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    (repo / "src/aictx").mkdir(parents=True)
    (repo / "src/aictx/continuity.py").write_text("def build_resume_capsule():\n    pass\n", encoding="utf-8")
    start_work_state(
        repo,
        "Refactor continuity ranking",
        initial={"next_action": "inspect src/aictx/continuity.py", "active_files": ["src/aictx/continuity.py"]},
    )
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "adjust docs wording", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["capsule"]["first_action"]["path"] == "src/aictx/continuity.py"
    assert payload["continuity_match"]["signals"][0] == "active_work_state"


def test_resume_partial_handoff_overlap_can_inform_but_not_dominate_more_specific_repomap(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "src/aictx/continuity/__init__.py": ("python", "resume_entry_points"),
            "tests/test_resume_command.py": ("python", "test_resume_entry_points"),
            "docs/continuity.md": ("markdown", "continuity notes"),
        },
    )
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Worked on continuity docs and entry points.",
            "recommended_starting_points": ["docs/continuity.md"],
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "fix continuity entry points ranking", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["capsule"]["first_action"]["path"] in {
        "src/aictx/continuity/__init__.py",
        "tests/test_resume_command.py",
    }
    assert any(item["path"] == "docs/continuity.md" for item in payload["capsule"]["entry_points"] + payload["capsule"]["fallback_entry_points"])
    assert any("task_driven_repomap" in signal or signal == "repomap_term_overlap" for signal in payload["continuity_match"]["signals"])


def test_resume_arbiter_can_promote_true_continuation_handoff(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished parser thread cleanup.",
            "recommended_starting_points": ["src/taskflow/parser.py"],
        },
    )
    script = tmp_path / "arbiter.py"
    _arbiter_script(script)
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_ENABLED", "1")
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", f"{sys.executable} {script}")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "Continue previous task: finish follow-up", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["capsule"]["first_action"]["path"] == "src/taskflow/parser.py"
    assert "llm_arbiter:keep" in payload["capsule"]["entry_points"][0]["reason"]


def test_resume_arbiter_can_demote_handoff_when_task_is_new(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "src/aictx/continuity/__init__.py": ("python", "build_resume_capsule"),
        },
    )
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")
    script = tmp_path / "arbiter.py"
    _arbiter_script(script)
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_ENABLED", "1")
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", f"{sys.executable} {script}")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    handoff_fallback = next(item for item in payload["capsule"]["fallback_entry_points"] if item["path"] == ".github/workflows/github-release.yml")
    assert "llm_arbiter:ignore" in handoff_fallback["reason"]
    assert payload["capsule"]["first_action"]["path"] == "src/aictx/continuity/__init__.py"


def test_resume_arbiter_can_use_official_adapter_wrapper_without_explicit_command(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(repo, {"src/aictx/continuity/__init__.py": ("python", "build_resume_capsule")})
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")
    script = tmp_path / "arbiter.py"
    _arbiter_script(script)
    monkeypatch.setattr("aictx.adapters.ENGINE_HOME", tmp_path / ".aictx")
    monkeypatch.setattr("aictx.adapters.GLOBAL_ADAPTERS_DIR", (tmp_path / ".aictx" / "adapters"))
    monkeypatch.setattr("aictx.adapters.GLOBAL_ADAPTERS_REGISTRY_PATH", (tmp_path / ".aictx" / "adapters" / "registry.json"))
    monkeypatch.setattr("aictx.adapters.GLOBAL_ADAPTERS_BIN_DIR", (tmp_path / ".aictx" / "adapters" / "bin"))
    monkeypatch.setattr("aictx.adapters.GLOBAL_ADAPTERS_INSTALL_STATUS_PATH", (tmp_path / ".aictx" / "adapters" / "install_status.json"))
    install_global_adapters()
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_ENABLED", "1")
    monkeypatch.delenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", raising=False)
    monkeypatch.setenv("AICTX_CODEX_ENTRYPOINT_ARBITER_COMMAND", f"{sys.executable} {script}")

    args = _parser().parse_args([
        "resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json", "--agent-id", "codex", "--adapter-id", "codex"
    ])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    handoff_fallback = next(item for item in payload["capsule"]["fallback_entry_points"] if item["path"] == ".github/workflows/github-release.yml")
    assert "llm_arbiter:ignore" in handoff_fallback["reason"]


def test_resume_arbiter_unset_uses_deterministic_fallback(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(repo, {"src/aictx/continuity/__init__.py": ("python", "build_resume_capsule")})
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")
    monkeypatch.delenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", raising=False)
    monkeypatch.delenv("AICTX_CODEX_ENTRYPOINT_ARBITER_COMMAND", raising=False)
    monkeypatch.delenv("AICTX_ENTRYPOINT_ARBITER_ENABLED", raising=False)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json"])
    assert args.func(args) == 0

    captured = capsys.readouterr()
    payload = json.loads(captured.out)
    reasons = " ".join(item["reason"] for item in payload["capsule"]["entry_points"] + payload["capsule"]["fallback_entry_points"])
    assert "llm_arbiter:" not in reasons
    assert captured.err == ""


@pytest.mark.parametrize(
    "script_source",
    [
        "print('not json')",
        "import sys\nsys.exit(42)",
    ],
)
def test_resume_arbiter_bad_output_or_nonzero_falls_back(tmp_path: Path, capsys, monkeypatch, script_source: str):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(repo, {"src/aictx/continuity/__init__.py": ("python", "build_resume_capsule")})
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")
    script = tmp_path / "bad_arbiter.py"
    _write_python_script(script, script_source)
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", f"{sys.executable} {script}")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json"])
    assert args.func(args) == 0

    captured = capsys.readouterr()
    payload = json.loads(captured.out)
    handoff_fallback = next(item for item in payload["capsule"]["fallback_entry_points"] if item["path"] == ".github/workflows/github-release.yml")
    assert "llm_arbiter:" not in handoff_fallback["reason"]
    assert payload["capsule"]["first_action"]["path"] == "src/aictx/continuity/__init__.py"
    assert captured.err == ""


def test_resume_arbiter_timeout_falls_back(tmp_path: Path, capsys, monkeypatch):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(repo, {"src/aictx/continuity/__init__.py": ("python", "build_resume_capsule")})
    write_json(
        repo / HANDOFF_PATH,
        {
            "status": "completed",
            "summary": "Finished release workflow cleanup.",
            "recommended_starting_points": [".github/workflows/github-release.yml"],
        },
    )
    (repo / ".github/workflows").mkdir(parents=True)
    (repo / ".github/workflows/github-release.yml").write_text("name: release\n", encoding="utf-8")
    script = tmp_path / "slow_arbiter.py"
    _write_python_script(script, "import time\ntime.sleep(1)\nprint('{}')")
    monkeypatch.setenv("AICTX_ENTRYPOINT_ARBITER_COMMAND", f"{sys.executable} {script}")
    monkeypatch.setattr("aictx.continuity.ENTRYPOINT_ARBITER_TIMEOUT_SECONDS", 0.01)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "refactor resume entry point ranking", "--json"])
    assert args.func(args) == 0

    captured = capsys.readouterr()
    payload = json.loads(captured.out)
    handoff_fallback = next(item for item in payload["capsule"]["fallback_entry_points"] if item["path"] == ".github/workflows/github-release.yml")
    assert "llm_arbiter:" not in handoff_fallback["reason"]
    assert payload["capsule"]["first_action"]["path"] == "src/aictx/continuity/__init__.py"
    assert captured.err == ""


def test_resume_documentation_task_prefers_readme_over_code(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "README.md": ("markdown", "Install instructions"),
            "src/payments/validation.py": ("python", "validate_payment"),
            "tests/test_payment_validation.py": ("python", "test_payment_validation"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "update README install instructions", "--json"])
    assert args.func(args) == 0

    assert json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"] == "README.md"


def test_resume_docs_task_contract_allows_readme_first_action(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "README.md": ("markdown", "Install instructions"),
            "src/payments/validation.py": ("python", "validate_payment"),
            "tests/test_payment_validation.py": ("python", "test_payment_validation"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "Update README install instructions", "--json"])
    assert args.func(args) == 0

    contract = json.loads(capsys.readouterr().out)["execution_contract"]
    assert contract["first_action"]["path"] == "README.md"
    assert contract["first_action"]["binding"] == "must_open_first"
    assert "README.md" in contract["edit_scope"]["primary"]
    assert "read README.md" not in contract["forbidden_before_first_edit"]


def test_resume_config_task_can_choose_config_or_ci(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            "README.md": ("markdown", "Configuration docs"),
            "pyproject.toml": ("toml", "pytest config"),
            ".github/workflows/test.yml": ("yaml", "pytest workflow"),
            "src/aictx/cli.py": ("python", "build_parser"),
            "tests/test_cli.py": ("python", "test_cli"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "adjust pytest config", "--json"])
    assert args.func(args) == 0

    assert json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"] in {
        "pyproject.toml",
        ".github/workflows/test.yml",
    }


def test_resume_metrics_analysis_task_can_choose_metrics(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            ".demo_metrics/with_aictx_v5/session_2_metrics.json": ("json", "session metrics"),
            ".demo_metrics/with_aictx_v5/codex_usage_session_2.json": ("json", "codex usage"),
            "src/taskflow/parser.py": ("python", "parse_tasks"),
            "tests/test_parser.py": ("python", "test_parser"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "analyze demo metrics and compare token usage", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    paths = [payload["capsule"]["first_action"]["path"]]
    paths.extend(item["path"] for item in payload["capsule"]["repo_map"]["primary"] + payload["capsule"]["repo_map"]["secondary"])
    assert any(path.startswith(".demo_metrics/") for path in paths)


def test_resume_normal_coding_task_does_not_choose_metrics(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_generic_repomap(
        repo,
        {
            ".demo_metrics/with_aictx_v5/session_2_metrics.json": ("json", "parser metrics"),
            ".demo_metrics/with_aictx_v5/codex_usage_session_2.json": ("json", "parser usage"),
            "src/taskflow/parser.py": ("python", "parse_tasks"),
            "tests/test_parser.py": ("python", "test_parser_edge_cases"),
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0

    first_path = json.loads(capsys.readouterr().out)["capsule"]["first_action"]["path"]
    assert not first_path.startswith(".demo_metrics/")
    assert "metrics" not in first_path


def test_resume_contract_checks_present(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0

    checks = json.loads(capsys.readouterr().out)["contract_checks"]
    assert checks["expected_first_action"] == "open:first_action.path"
    assert checks["expected_test_command"]
    assert checks["expected_final_command"] == "aictx finalize"
    for violation in [
        "missing_finalize",
        "repo_wide_orientation_before_first_edit",
        "alternative_test_command_before_canonical_test",
    ]:
        assert violation in checks["violations_to_report"]




def test_finalize_cli_accepts_task_for_contract_evaluation(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    (repo / "Makefile").write_text("test:\n\tpytest -q\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0
    capsys.readouterr()

    args = _parser().parse_args([
        "finalize",
        "--repo", str(repo),
        "--status", "success",
        "--summary", "validated parser edge cases",
        "--task", "validate parser edge cases",
        "--files-opened", "tests/test_parser.py",
        "--files-edited", "tests/test_parser.py",
        "--commands-executed", "make test",
        "--tests-executed", "make test",
        "--json",
    ])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["contract_compliance"]["status"] == "followed"
    assert payload["contract_compliance"]["task_goal"] == "validate parser edge cases"

def test_finalize_records_contract_adherence_success(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    (repo / "Makefile").write_text("test:\n\tpytest -q\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0
    capsys.readouterr()

    prepared = prepare_execution(
        {
            "repo_root": str(repo),
            "user_request": "validate parser edge cases",
            "agent_id": "codex",
            "execution_id": "exec-contract-ok",
            "files_opened": ["tests/test_parser.py"],
            "files_edited": ["tests/test_parser.py"],
            "commands_executed": ["make test"],
            "tests_executed": ["make test"],
        }
    )
    finalized = finalize_execution(prepared, {"success": True, "result_summary": "validated parser edge cases"})

    adherence = finalized["contract_adherence"]
    assert adherence["contract_available"] is True
    assert adherence["opened_first_action"] is True
    assert adherence["edited_within_scope"] is True
    assert adherence["canonical_test_used"] is True
    assert adherence["finalize_called"] is True
    assert adherence["violations"] == []
    assert finalized["aictx_feedback"]["contract_adherence"] == adherence
    assert finalized["agent_summary"]["contract_adherence"] == adherence
    assert finalized["value_evidence"]["contract_adherence"] == adherence


def test_finalize_records_contract_adherence_violations(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_parser_fixture(repo)
    (repo / "Makefile").write_text("test:\n\tpytest -q\n", encoding="utf-8")

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "validate parser edge cases", "--json"])
    assert args.func(args) == 0
    capsys.readouterr()

    prepared = prepare_execution(
        {
            "repo_root": str(repo),
            "user_request": "validate parser edge cases",
            "agent_id": "codex",
            "execution_id": "exec-contract-violations",
            "files_opened": ["README.md"],
            "files_edited": ["README.md"],
            "commands_executed": ["git status", "python -m pytest -q"],
            "tests_executed": ["python -m pytest -q"],
        }
    )
    finalized = finalize_execution(prepared, {"success": True, "result_summary": "validated parser edge cases"})

    adherence = finalized["contract_adherence"]
    assert adherence["contract_available"] is True
    assert adherence["opened_first_action"] is False
    assert adherence["edited_within_scope"] is False
    assert adherence["canonical_test_used"] is False
    for violation in [
        "missing_first_action_open",
        "edit_outside_scope",
        "edited_avoided_path",
        "canonical_test_not_observed",
        "git_status_before_first_edit",
        "alternative_test_command_before_canonical_test",
    ]:
        assert violation in adherence["violations"]


def test_resume_completed_previous_task_is_background(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    start_work_state(repo, "Old task", initial={"next_action": "done"})
    close_work_state(repo, status="resolved")
    write_json(repo / HANDOFF_PATH, {"summary": "Old task finished.", "completed": ["done"], "next_steps": ["continue old task"], "recommended_starting_points": []})

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "new task", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["task_state"]["status"] == "completed"
    assert "background" in payload["capsule"]["resuming"]
    assert payload["capsule"]["current_request"] == "new task"
    assert payload["capsule"]["next_action"] != "continue old task"


def test_resume_paused_work_state_carryover_wins_over_completed_handoff(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    start_work_state(
        repo,
        "Validate parser carryover",
        initial={
            "unverified": ["Contract gap (needs-validation): Canonical test command was not observed."],
            "next_action": "run expected validation command: make test",
            "contract_gaps": [
                {
                    "kind": "missing_validation",
                    "severity": "needs-validation",
                    "policy": "prioritize_before_new_work",
                    "blocking": False,
                    "summary": "Canonical test command was not observed.",
                    "recommended_command": "make test",
                    "next_action": "run expected validation command: make test",
                }
            ],
        },
    )
    close_work_state(repo, status="paused")
    write_json(repo / HANDOFF_PATH, {"status": "completed", "summary": "Old handoff finished.", "completed": ["old done"], "next_steps": ["continue old task"]})

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "continue parser validation", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["task_state"]["status"] == "blocked"
    assert payload["capsule"]["next_action"] == "run expected validation command: make test"
    assert payload["strongest_carryover_gap"]["severity"] == "needs-validation"
    assert payload["carryover_gaps"][0]["policy"] == "prioritize_before_new_work"
    assert "needs-validation" in payload["task_state"]["reason"]
    assert payload["loaded_context"][0]["kind"] == "work_state"
    assert payload["loaded_context"][0]["role"] == "carryover"
    assert payload["loaded_context"][0]["selection_reason"]
    assert "contract_gap:needs-validation" in payload["loaded_context"][0]["match_reasons"]
    assert any(item["kind"] == "handoff" and item["role"] == "background" for item in payload["loaded_context"])


def test_resume_missing_entry_point_lowers_confidence_and_uses_fallback(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_repomap(repo)
    write_json(
        repo / HANDOFF_PATH,
        {
            "summary": "Continue resume command.",
            "recommended_starting_points": ["src/aictx/missing.py"],
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "build resume capsule", "--json"])
    assert args.func(args) == 0

    payload = json.loads(capsys.readouterr().out)
    assert payload["task_state"]["confidence"] in {"low", "medium"}
    assert "missing_entry_point:src/aictx/missing.py" in payload["warnings"]
    assert payload["capsule"]["entry_points"]
    assert payload["capsule"]["entry_points"][0]["path"] != "src/aictx/missing.py"


def test_resume_repomap_slice_has_primary_and_secondary(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_repomap(repo)

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "resume command", "--json"])
    assert args.func(args) == 0

    repo_map = json.loads(capsys.readouterr().out)["capsule"]["repo_map"]
    assert repo_map["primary"][0]["path"] in {"src/aictx/continuity.py", "tests/test_resume_command.py"}
    assert repo_map["secondary"]



def test_finalize_json_smoke(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)

    args = _parser().parse_args(["finalize", "--repo", str(repo), "--status", "success", "--summary", "Implemented parser edge tests", "--json"])
    assert args.func(args) == 0

    output = capsys.readouterr().out
    payload = json.loads(output)
    assert payload.get("agent_summary_text") or payload.get("agent_summary")
    assert "invalid choice" not in output


def test_finalize_text_smoke(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)

    args = _parser().parse_args(["finalize", "--repo", str(repo), "--status", "success", "--summary", "Done"])
    assert args.func(args) == 0

    output = capsys.readouterr().out
    assert "AICTX summary" in output or "AICTX summary unavailable" in output


def test_parser_accepts_finalize_regression(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    args = _parser().parse_args([
        "finalize",
        "--repo", str(repo),
        "--status", "success",
        "--summary", "Done",
        "--json",
    ])
    assert args.func(args) == 0
    assert json.loads(capsys.readouterr().out).get("agent_summary_text")

def test_advanced_help_lists_advanced_commands(capsys):
    with pytest.raises(SystemExit) as exc:
        _parser().parse_args(["advanced", "--help"])
    assert exc.value.code == 0
    output = capsys.readouterr().out
    for command in ["suggest", "reuse", "next", "task", "messages", "map", "report", "reflect", "internal"]:
        assert command in output
    assert 'aictx resume --repo . --task "<task goal>"' in output
    assert "finalize" not in [line.strip() for line in output.splitlines() if line.strip().startswith("-")]


def test_advanced_command_without_help_lists_commands(capsys):
    args = _parser().parse_args(["advanced"])
    assert args.func(args) == 0
    output = capsys.readouterr().out
    for command in ["suggest", "reuse", "next", "task", "messages", "map", "report", "reflect", "internal"]:
        assert f"- {command}:" in output
    assert 'aictx resume --repo . --task "<task goal>"' in output
    assert "aictx finalize --repo . --status success|failure" in output
    assert "- finalize:" not in output


def test_top_level_help_hides_advanced_commands(capsys):
    with pytest.raises(SystemExit) as exc:
        _parser().parse_args(["--help"])
    assert exc.value.code == 0
    output = capsys.readouterr().out
    assert "resume" in output
    assert "finalize" in output
    assert "advanced" in output
    assert "{install,init,portability,resume,finalize,view,doctor,advanced,clean,uninstall}" in output
    for command in ["suggest", "reuse", "next", "task", "messages", "map", "report", "reflect", "internal"]:
        assert f"    {command}" not in output


def test_runtime_contract_says_resume_does_not_replace_lifecycle():
    text = render_agent_runtime()
    for term in [
        "Render exactly one startup banner source",
        "resume.startup_banner_text",
        "resume.startup_banner_render_payload",
        "prepare_execution().startup_banner_text",
        "prepare_execution().startup_banner_render_payload",
        "Do not render both",
        "does not replace",
        "finalize",
        "final AICTX summary",
        "aictx resume --repo .",
        "aictx finalize --repo .",
        "finalize_execution is the middleware API behind that command",
        "do not call it directly from the shell",
        "Do not run `aictx internal execution finalize`",
        "Do not render both",
        "--json",
        "prepare/startup context → resume capsule → work → finalize → final AICTX summary/persistence",
    ]:
        assert term in text


def test_resume_startup_source_and_finalize_summary_source_are_separate(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    write_json(repo / HANDOFF_PATH, {"summary": "resume source check", "completed": ["source check done"]})

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "source check", "--json", "--agent-id", "codex", "--session-id", "visible-resume"])
    assert args.func(args) == 0
    resume_payload = json.loads(capsys.readouterr().out)
    assert "Resuming: resume source check." in resume_payload["startup_banner_text"]
    assert resume_payload["startup_banner_render_payload"]["canonical_text"] == resume_payload["startup_banner_text"]

    prepared = prepare_execution(
        {
            "repo_root": str(repo),
            "user_request": "source check",
            "agent_id": "codex",
            "adapter_id": "codex",
            "execution_id": "exec-source-check",
            "session_id": "visible-prepare",
            "files_opened": ["src/aictx/continuity.py"],
        }
    )
    finalized = finalize_execution(prepared, {"success": True, "result_summary": "Verified source separation."})
    assert finalized["agent_summary_text"].startswith("────────────────────────────────\nAICTX summary\n")
    assert finalized["agent_summary_policy"]["render_payload_field"] == "agent_summary_render_payload"


def test_resume_excludes_aictx_paths_from_action_candidates(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    (repo / ".aictx/continuity").mkdir(parents=True, exist_ok=True)
    (repo / ".aictx/continuity/resume_capsule.md").write_text("# generated", encoding="utf-8")
    (repo / "src").mkdir()
    (repo / "src/resume.py").write_text("def resume_capsule():\n    pass\n", encoding="utf-8")
    write_json(repo / HANDOFF_PATH, {"summary": "generated capsule", "recommended_starting_points": [".aictx/continuity/resume_capsule.md", "src/resume.py"]})
    write_repomap_config(repo, {"enabled": True})
    write_repomap_index(
        repo,
        {
            "version": 1,
            "files": [
                {"path": ".aictx/continuity/resume_capsule.md", "language": "markdown", "symbols": [{"name": "resume capsule", "kind": "heading", "line": 1, "language": "markdown"}]},
                {"path": "src/resume.py", "language": "python", "symbols": [{"name": "resume_capsule", "kind": "function", "line": 1, "language": "python"}]},
            ],
        },
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "resume capsule", "--json"])
    assert args.func(args) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["capsule"]["first_action"]["path"] == "src/resume.py"
    paths = []
    paths.append(payload["capsule"]["first_action"]["path"])
    paths.extend(item["path"] for item in payload["capsule"]["entry_points"])
    paths.extend(item["path"] for item in payload["capsule"]["fallback_entry_points"])
    repo_map = payload["capsule"]["repo_map"]
    paths.extend(item["path"] for item in repo_map["primary"] + repo_map["secondary"])
    assert paths
    assert all(not path.startswith(".aictx/") for path in paths)


def test_rich_resume_fixture_stays_compact_and_compiled(tmp_path: Path, capsys):
    repo = tmp_path / "repo"
    init_repo_scaffold(repo, update_gitignore=False)
    _seed_repomap(repo)
    start_work_state(
        repo,
        "Build resume capsule",
        initial={
            "next_action": "update src/aictx/continuity.py",
            "active_files": ["src/aictx/continuity.py"],
            "verified": [f"verified item {index}" for index in range(8)],
        },
    )
    write_json(
        repo / HANDOFF_PATH,
        {
            "summary": "Previous resume capsule work " * 40,
            "completed": [f"completed resume step {index}" for index in range(12)],
            "next_steps": [f"next resume step {index}" for index in range(12)],
            "recommended_starting_points": ["src/aictx/continuity.py"],
        },
    )
    (repo / LAST_EXECUTION_SUMMARY_PATH).write_text("# Summary\n\n" + ("large summary\n" * 100), encoding="utf-8")
    _write_jsonl(
        repo / DECISIONS_PATH,
        [{"decision": f"Resume decision {index}", "related_paths": ["src/aictx/continuity.py"], "subsystem": "resume"} for index in range(10)],
    )
    _write_jsonl(
        repo / REPO_FAILURE_MEMORY_DIR / "failure_patterns.jsonl",
        [
            {
                "failure_id": f"failure-{index}",
                "signature": f"resume capsule failure {index}",
                "failure_signature": f"resume capsule failure {index}",
                "task_type": "feature_work",
                "status": "open",
                "error_text": f"resume capsule error {index}",
                "related_paths": ["src/aictx/continuity.py"],
            }
            for index in range(10)
        ],
    )
    _write_jsonl(
        repo / REPO_STRATEGY_MEMORY_DIR / "strategies.jsonl",
        [
            {
                "task_id": "resume-strategy",
                "task_text": "resume capsule",
                "task_type": "feature_work",
                "entry_points": ["src/aictx/continuity.py", "tests/test_resume_command.py"],
                "files_used": ["src/aictx/continuity.py", "tests/test_resume_command.py"],
                "commands_executed": ["pytest tests/test_resume_command.py"],
                "tests_executed": ["tests/test_resume_command.py"],
                "success": True,
            }
        ],
    )

    args = _parser().parse_args(["resume", "--repo", str(repo), "--task", "resume capsule", "--task-type", "feature_work"])
    assert args.func(args) == 0
    output = capsys.readouterr().out
    assert len(output) <= 6000
    for section in ["Startup rule", "First action", "Current request", "Task state", "Next action", "Entry points", "Relevant RepoMap", "Relevant failures", "Relevant decisions", "Strategy", "Avoid"]:
        assert section in output
    assert '{"' not in output
    payload = json.loads((repo / RESUME_CAPSULE_JSON_PATH).read_text(encoding="utf-8"))
    assert payload["sources"]["handoff"]
    assert payload["sources"]["last_execution_summary"]
    assert payload["sources"]["work_state"]
    assert payload["sources"]["repo_map"]
    assert payload["capsule"]["failures"]
    assert payload["capsule"]["decisions"]
    assert payload["capsule"]["strategy"] != "None relevant"


def test_resume_capsules_are_local_generated_in_portable_mode(tmp_path: Path):
    repo = tmp_path / "repo"
    repo.mkdir()
    subprocess.run(["git", "init"], cwd=repo, text=True, capture_output=True, check=True)
    init_repo_scaffold(repo, portable_continuity=True)
    for rel_path in [RESUME_CAPSULE_MARKDOWN_PATH.as_posix(), RESUME_CAPSULE_JSON_PATH.as_posix()]:
        path = repo / rel_path
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text("generated", encoding="utf-8")
        completed = subprocess.run(["git", "check-ignore", rel_path], cwd=repo, text=True, capture_output=True, check=False)
        assert completed.returncode == 0, rel_path


def test_public_docs_prefer_task_for_normal_startup():
    readme = Path("README.md").read_text(encoding="utf-8")
    technical = Path("docs/TECHNICAL_OVERVIEW.md").read_text(encoding="utf-8")
    forbidden = 'aictx resume --repo . --request "<current user request>"'
    assert forbidden not in readme
    assert forbidden not in technical
    assert 'aictx resume --repo . --task "<task goal>" --json' in technical
    assert "--request` remains supported" not in readme
    assert "--request` remains supported" not in technical
