"""Tests for ``mgf-common doctor --standards``.

Builds a tiny project tree per test (pyproject.toml + src/<pkg>/)
under ``tmp_path``, then runs each check against it. Avoids
shelling out via the CLI — exercises the audit functions directly
so failures point at the right line.

Covers:
  - Project shape discovery (flat + namespace layouts).
  - AP-09 py.typed marker presence / absence.
  - AP-13 __version__ ↔ pyproject sync (match / mismatch /
    missing).
  - TS-04 filterwarnings escalation (configured / missing /
    not-error-first).
  - DP-12 naive datetime detection.
  - End-to-end ``doctor_standards()`` exit code.
"""

from __future__ import annotations

import shutil
import subprocess
from typing import TYPE_CHECKING

import pytest

from mgf.fed.cli import _doctor_standards as _ds
from mgf.fed.cli._doctor_standards import (
    _check_commit_hook,
    _check_conformance_ledger_complete,
    _check_filterwarnings,
    _check_gitignore_my_stuff,
    _check_gitignore_org_policy_travels,
    _check_lesson_learned_present,
    _check_memory_dir,
    _check_memory_store_vcs,
    _check_naive_datetime,
    _check_propagation_headers,
    _check_py_typed_marker,
    _check_supervisor_coverage_patch,
    _check_version_sync,
    _corpus_must_ids,
    _discover_project,
    _ignores_org_policy,
    doctor_standards,
)

if TYPE_CHECKING:
    from collections.abc import Mapping
    from pathlib import Path


def _write_project(
    root: Path,
    *,
    package_name: str = "myapp",
    declared_version: str = "1.0.0",
    runtime_version: str | None = "1.0.0",
    py_typed: bool = True,
    filterwarnings: list[str] | None = ("error",),
    extra_pyproject: Mapping[str, object] | None = None,
    package_files: Mapping[str, str] | None = None,
    complete_ledger: bool = True,
    lesson_learned: bool = True,
    memory_md: bool = True,
    gitignore_my_stuff: bool = True,
    prepare_commit_hook: bool = True,
) -> Path:
    """Build a minimal project tree at ``root``; return ``root``.

    By default also writes a COMPLETE ``MGF_STANDARDS_CONFORMANCE.md`` (every
    corpus MUST as an N/A row) so the DOC-15 ledger-completeness check passes —
    pass ``complete_ledger=False`` to omit it.

    Defaults to a fully conformant project (AG-17 docs/memory/MEMORY.md +
    AG-18 my_stuff/-ignoring .gitignore both seeded) so callers that build a
    clean tree stay green; pass ``memory_md=False`` / ``gitignore_my_stuff=False``
    to omit each.
    """
    underscore = package_name.replace("-", "_")
    (root / "src" / underscore).mkdir(parents=True, exist_ok=True)
    if py_typed:
        (root / "src" / underscore / "py.typed").touch()
    init = root / "src" / underscore / "__init__.py"
    if runtime_version is not None:
        init.write_text(f'__version__ = "{runtime_version}"\n')
    else:
        init.write_text("")
    for rel, content in (package_files or {}).items():
        (root / "src" / underscore / rel).write_text(content)

    pyproject_text = f'[project]\nname = "{package_name}"\n'
    if declared_version:
        pyproject_text += f'version = "{declared_version}"\n'
    if filterwarnings is not None:
        # Render as a TOML array literal.
        items = ", ".join(f'"{f}"' for f in filterwarnings)
        pyproject_text += f"\n[tool.pytest.ini_options]\nfilterwarnings = [{items}]\n"
    (root / "pyproject.toml").write_text(pyproject_text)
    if complete_ledger:
        _write_complete_ledger(root)
    if lesson_learned:
        # DOC-19 (MUST since standards v2.21) — a clean project ships LESSON_LEARNED.md.
        (root / "LESSON_LEARNED.md").write_text("# LESSON_LEARNED\n", encoding="utf-8")
    if memory_md:
        # AG-17 (MUST since standards v2.22) — committed in-repo agent memory.
        mem = root / "docs" / "memory"
        mem.mkdir(parents=True, exist_ok=True)
        (mem / "MEMORY.md").write_text("# Memory index\n", encoding="utf-8")
    if gitignore_my_stuff:
        # AG-18 (MUST since standards v2.22) — .gitignore ignores the local secrets dir.
        (root / ".gitignore").write_text("my_stuff/\n", encoding="utf-8")
    if prepare_commit_hook:
        # AG-18 (commit-trailer enforcement, MUST since standards v2.24) — the
        # committed prepare-commit-msg hook. Presence is the bar (doctor checks
        # `.githooks/prepare-commit-msg` exists).
        hooks = root / ".githooks"
        hooks.mkdir(parents=True, exist_ok=True)
        (hooks / "prepare-commit-msg").write_text("#!/bin/sh\n", encoding="utf-8")
    return root


def _write_complete_ledger(root: Path) -> None:
    """Write a conformance ledger that accounts for every corpus MUST (N/A
    rows) so the DOC-15 completeness check is satisfied. Ids are derived from
    the same source the check reads, so the fixture tracks the corpus."""
    ids = _corpus_must_ids() or set()
    rows = "\n".join(f"| {rid} | N/A | fixture |" for rid in sorted(ids))
    led = root / "docs" / "inprogress"
    led.mkdir(parents=True, exist_ok=True)
    (led / "MGF_STANDARDS_CONFORMANCE.md").write_text(
        "# MGF standards conformance\n\n| ID | Status | Notes |\n|---|---|---|\n" + rows + "\n",
        encoding="utf-8",
    )


# ---------------------------------------------------------------------------
# Project shape discovery
# ---------------------------------------------------------------------------


class TestProjectDiscovery:
    def test_flat_layout(self, tmp_path: Path) -> None:
        _write_project(tmp_path, package_name="myapp")
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_name == "myapp"
        assert shape.package_root.name == "myapp"

    def test_namespace_layout(self, tmp_path: Path) -> None:
        # Distribution name "mgf-common" → import path "mgf/common".
        (tmp_path / "src" / "mgf" / "common").mkdir(parents=True)
        (tmp_path / "src" / "mgf" / "common" / "__init__.py").write_text(
            '__version__ = "0.1.0"\n',
        )
        (tmp_path / "src" / "mgf" / "common" / "py.typed").touch()
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "mgf-common"\nversion = "0.1.0"\n',
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_name == "mgf-common"
        assert shape.package_root == tmp_path / "src" / "mgf" / "common"

    def test_no_pyproject_returns_none(self, tmp_path: Path) -> None:
        # Empty dir.
        assert _discover_project(tmp_path) is None

    def test_flat_layout_returns_shape_no_package(self, tmp_path: Path) -> None:
        # Flat / multi-package layout (e.g. a Django project: config/ + apps/ at
        # the repo root) — still a shape (root-level checks run), but no single
        # import package, so package_root is None.
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "myapp"\n')
        (tmp_path / "config").mkdir()
        (tmp_path / "config" / "__init__.py").touch()
        (tmp_path / "apps").mkdir()
        (tmp_path / "apps" / "__init__.py").touch()
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_root is None
        assert shape.src_dir is None

    def test_descend_single_child_namespace(self, tmp_path: Path) -> None:
        # dist `vm-vmanager-agent` → package src/vmanager/agent (vmanager is a
        # bare namespace whose only child is the real package).
        pkg = tmp_path / "src" / "vmanager" / "agent"
        pkg.mkdir(parents=True)
        (pkg / "__init__.py").write_text('__version__ = "0.1.0"\n')
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "vm-vmanager-agent"\nversion = "0.1.0"\n'
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_root == pkg

    def test_no_pyproject_still_none(self, tmp_path: Path) -> None:
        (tmp_path / "config").mkdir()
        assert _discover_project(tmp_path) is None


# ---------------------------------------------------------------------------
# Individual checks
# ---------------------------------------------------------------------------


class TestPyTypedCheck:
    def test_present(self, tmp_path: Path) -> None:
        _write_project(tmp_path, py_typed=True)
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_py_typed_marker(shape).tier == "ok"

    def test_missing(self, tmp_path: Path) -> None:
        _write_project(tmp_path, py_typed=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_py_typed_marker(shape)
        assert finding.tier == "fail"
        assert "py.typed" in finding.message


class TestVersionSyncCheck:
    def test_match(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            declared_version="1.2.3",
            runtime_version="1.2.3",
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_version_sync(shape).tier == "ok"

    def test_dynamic_importlib_metadata_version_ok(self, tmp_path: Path) -> None:
        """AP-13 — ``__version__`` resolved at runtime via importlib.metadata
        (the drift-proof "always the installed dist" pattern) is conformant;
        the not-installed fallback literal MUST NOT be read as a drift."""
        _write_project(
            tmp_path,
            declared_version="1.2.3",
            package_files={
                "__init__.py": (
                    "from importlib.metadata import PackageNotFoundError\n"
                    "from importlib.metadata import version as _pkg_version\n"
                    "try:\n"
                    '    __version__ = _pkg_version("myapp")\n'
                    "except PackageNotFoundError:\n"
                    '    __version__ = "0.0.0+unknown"\n'
                ),
            },
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_version_sync(shape)
        assert finding.tier == "ok"
        assert "importlib.metadata" in finding.message
        # The not-installed fallback literal must NOT surface as a drift.
        assert "0.0.0+unknown" not in finding.message

    def test_mismatch(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            declared_version="1.2.3",
            runtime_version="9.9.9",
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_version_sync(shape)
        assert finding.tier == "fail"
        assert "1.2.3" in finding.message
        assert "9.9.9" in finding.message

    def test_missing_runtime_version(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            declared_version="1.2.3",
            runtime_version=None,
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_version_sync(shape)
        assert finding.tier == "warn"

    def test_missing_declared_version(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            declared_version="",
            runtime_version="1.0.0",
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_version_sync(shape)
        assert finding.tier == "warn"


class TestFilterwarningsCheck:
    def test_configured_with_error(self, tmp_path: Path) -> None:
        _write_project(tmp_path, filterwarnings=["error"])
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_filterwarnings(shape).tier == "ok"

    def test_missing(self, tmp_path: Path) -> None:
        _write_project(tmp_path, filterwarnings=None)
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_filterwarnings(shape)
        assert finding.tier == "fail"

    def test_no_error_entry(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            filterwarnings=["ignore::DeprecationWarning"],
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_filterwarnings(shape)
        assert finding.tier == "fail"
        assert "error" in finding.message


class TestNaiveDatetimeCheck:
    def test_no_violations(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            package_files={
                "module.py": ("from datetime import UTC, datetime\nx = datetime.now(UTC)\n"),
            },
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_naive_datetime(shape).tier == "ok"

    def test_utcnow_flagged(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            package_files={
                "module.py": ("from datetime import datetime\nx = datetime.utcnow()\n"),
            },
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_naive_datetime(shape)
        assert finding.tier == "fail"
        assert "utcnow" in finding.message

    def test_naive_now_flagged(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            package_files={
                "module.py": ("from datetime import datetime\nx = datetime.now()\n"),
            },
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        finding = _check_naive_datetime(shape)
        assert finding.tier == "fail"

    def test_now_with_tz_kwarg_passes(self, tmp_path: Path) -> None:
        _write_project(
            tmp_path,
            package_files={
                "module.py": ("from datetime import UTC, datetime\nx = datetime.now(tz=UTC)\n"),
            },
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        # Currently, we accept positional too; the AST check looks
        # for either kwarg `tz=...` OR positional args.
        assert _check_naive_datetime(shape).tier == "ok"


# ---------------------------------------------------------------------------
# End-to-end exit code
# ---------------------------------------------------------------------------


class TestDoctorStandardsExitCode:
    def test_clean_project_exits_zero(
        self,
        tmp_path: Path,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        _write_project(tmp_path)
        rc = doctor_standards(root=tmp_path)
        assert rc == 0

    def test_failing_project_exits_nonzero(
        self,
        tmp_path: Path,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        _write_project(tmp_path, py_typed=False)  # AP-09 will FAIL
        rc = doctor_standards(root=tmp_path)
        assert rc != 0

    def test_no_project_exits_nonzero(
        self,
        tmp_path: Path,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        rc = doctor_standards(root=tmp_path)
        assert rc != 0


class TestPropagationHeaders:
    """DOC-18 — owned rule-defining standards docs MUST carry a
    Propagation header."""

    @staticmethod
    def _std(root: Path, name: str, body: str) -> None:
        d = root / "docs" / "standards"
        d.mkdir(parents=True, exist_ok=True)
        (d / name).write_text(body, encoding="utf-8")

    def test_na_when_no_standards_dir(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_propagation_headers(shape)
        assert f.tier == "ok"
        assert "N/A" in f.message

    def test_ok_when_rule_doc_has_header(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        self._std(
            tmp_path,
            "X.md",
            "# X\n\n## Rules box\n\n> **Propagation (DOC-18):** foo → bar\n",
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_propagation_headers(shape).tier == "ok"

    def test_fail_when_rule_doc_missing_header(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        self._std(tmp_path, "X.md", "# X\n\n## Rules box\n\n| ID | Rule |\n")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_propagation_headers(shape)
        assert f.tier == "fail"
        assert "X.md" in f.message

    def test_pointer_only_dir_is_na(self, tmp_path: Path) -> None:
        # A doc with no Rules box (e.g. a pointer README to the cornerstone)
        # does not define rules → not bound by DOC-18.
        _write_project(tmp_path)
        self._std(tmp_path, "README.md", "# Standards\n\nSee mgf-common.\n")
        self._std(tmp_path, "POINTER.md", "# Pointer\n\nLinks only.\n")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_propagation_headers(shape)
        assert f.tier == "ok"
        assert "N/A" in f.message


class TestConformanceLedgerComplete:
    """WU6 — the partial-sync gate: MGF_STANDARDS_CONFORMANCE.md must account
    for every current MUST in the corpus, else doctor --standards FAILS."""

    def test_corpus_must_ids_nonempty(self) -> None:
        ids = _corpus_must_ids()
        assert ids, "Index of MUSTs parsed empty"
        assert {"DP-01", "DOC-18", "EH-07"} <= ids

    def test_complete_ledger_passes(self, tmp_path: Path) -> None:
        _write_project(tmp_path)  # complete ledger written by default
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_conformance_ledger_complete(shape)
        assert f.tier == "ok", f.message

    def test_missing_ledger_fails(self, tmp_path: Path) -> None:
        _write_project(tmp_path, complete_ledger=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_conformance_ledger_complete(shape)
        assert f.tier == "fail"
        assert "not found" in f.message

    def test_partial_ledger_fails_listing_count(self, tmp_path: Path) -> None:
        _write_project(tmp_path, complete_ledger=False)
        led = tmp_path / "docs" / "inprogress"
        led.mkdir(parents=True, exist_ok=True)
        (led / "MGF_STANDARDS_CONFORMANCE.md").write_text(
            "| DP-01 | N/A | x |\n| DP-02 | N/A | x |\n", encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_conformance_ledger_complete(shape)
        assert f.tier == "fail"
        assert "unaccounted" in f.message


class TestLessonLearnedPresent:
    """DOC-19 / PAPER-99 — the LESSON_LEARNED.md presence gate. Corpus-aware:
    inert unless the resolved Index of MUSTs carries DOC-19 (promoted to a MUST
    in standards v2.21), so a project pinning an older corpus is unaffected."""

    _MOD = "mgf.fed.cli._doctor_standards._corpus_must_ids"

    def test_inert_when_doc19_not_in_corpus(self, tmp_path, monkeypatch) -> None:
        # Older pinned corpus (DOC-19 absent / still a SHOULD) → not required.
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "DOC-18"})
        _write_project(tmp_path, lesson_learned=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_lesson_learned_present(shape)  # no file created
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_fails_when_required_and_absent(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "DOC-19"})
        _write_project(tmp_path, lesson_learned=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_lesson_learned_present(shape)
        assert f.tier == "fail"
        assert "missing" in f.message

    def test_passes_when_required_and_present(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "DOC-19"})
        _write_project(tmp_path)
        (tmp_path / "LESSON_LEARNED.md").write_text("# LESSON_LEARNED\n", encoding="utf-8")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_lesson_learned_present(shape)
        assert f.tier == "ok"
        assert "present" in f.message

    def test_warn_when_corpus_unresolvable(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: None)
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_lesson_learned_present(shape)
        assert f.tier == "unknown"


class TestMemoryDir:
    """AG-17 — the docs/memory/MEMORY.md presence gate. Corpus-aware: inert
    unless the resolved Index of MUSTs carries AG-17 (a MUST since standards
    v2.22), so a project pinning an older corpus is unaffected."""

    _MOD = "mgf.fed.cli._doctor_standards._corpus_must_ids"

    def test_inert_when_ag17_not_in_corpus(self, tmp_path, monkeypatch) -> None:
        # Older pinned corpus (AG-17 absent) → not required.
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "DOC-18"})
        _write_project(tmp_path, memory_md=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_memory_dir(shape)  # no file created
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_fails_when_required_and_absent(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "AG-17"})
        _write_project(tmp_path, memory_md=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_memory_dir(shape)
        assert f.tier == "fail"
        assert "missing" in f.message

    def test_passes_when_required_and_present(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "AG-17"})
        _write_project(tmp_path)  # memory_md seeded by default
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_memory_dir(shape)
        assert f.tier == "ok"
        assert "present" in f.message

    def test_warn_when_corpus_unresolvable(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: None)
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_memory_dir(shape)
        assert f.tier == "unknown"


class TestMemoryStoreVcs:
    """AG-19 — the runtime auto-memory store must be git-tracked (allowlist
    .gitignore + off-machine remote). Corpus-aware: inert unless the resolved
    Index of MUSTs carries AG-19 (a MUST since standards v2.25). The store lives
    outside the repo, so the path is injected via _runtime_memory_dir."""

    _CORPUS = "mgf.fed.cli._doctor_standards._corpus_must_ids"
    _MEMDIR = "mgf.fed.cli._doctor_standards._runtime_memory_dir"

    def _shape(self, tmp_path, store):
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        return shape

    def _conformant_store(self, store) -> None:
        (store / ".git").mkdir(parents=True)
        (store / ".gitignore").write_text("*\n!*.md\n!.gitignore\n", encoding="utf-8")
        (store / ".git" / "config").write_text(
            '[remote "origin"]\n\turl = ssh://git@codeberg.org/x/proj-memory.git\n',
            encoding="utf-8",
        )

    def test_warn_when_corpus_unresolvable(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._CORPUS, lambda: None)
        shape = self._shape(tmp_path, tmp_path / "store")
        assert _check_memory_store_vcs(shape).tier == "unknown"

    def test_inert_when_ag19_not_in_corpus(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._CORPUS, lambda: {"DP-01", "AG-17"})
        shape = self._shape(tmp_path, tmp_path / "store")
        f = _check_memory_store_vcs(shape)
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_ok_when_required_but_no_store_yet(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._CORPUS, lambda: {"DP-01", "AG-19"})
        store = tmp_path / "store"  # never created
        monkeypatch.setattr(self._MEMDIR, lambda _root: store)
        shape = self._shape(tmp_path, store)
        f = _check_memory_store_vcs(shape)
        assert f.tier == "ok"
        assert "nothing to track" in f.message

    def test_fails_when_store_untracked(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._CORPUS, lambda: {"DP-01", "AG-19"})
        store = tmp_path / "store"
        store.mkdir()  # exists but no .git / .gitignore / remote
        monkeypatch.setattr(self._MEMDIR, lambda _root: store)
        shape = self._shape(tmp_path, store)
        f = _check_memory_store_vcs(shape)
        assert f.tier == "fail"
        assert "not under version control" in f.message
        assert "allowlist .gitignore" in f.message
        assert "off-machine git remote" in f.message

    def test_passes_when_store_conformant(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._CORPUS, lambda: {"DP-01", "AG-19"})
        store = tmp_path / "store"
        self._conformant_store(store)
        monkeypatch.setattr(self._MEMDIR, lambda _root: store)
        shape = self._shape(tmp_path, store)
        f = _check_memory_store_vcs(shape)
        assert f.tier == "ok"
        assert "git-tracked" in f.message


class TestGitignoreMyStuff:
    """AG-18 — the .gitignore-must-ignore-my_stuff/ gate. Corpus-aware: inert
    unless the resolved Index of MUSTs carries AG-18 (a MUST since standards
    v2.22), so a project pinning an older corpus is unaffected."""

    _MOD = "mgf.fed.cli._doctor_standards._corpus_must_ids"

    def test_inert_when_ag18_not_in_corpus(self, tmp_path, monkeypatch) -> None:
        # Older pinned corpus (AG-18 absent) → not required.
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "DOC-18"})
        _write_project(tmp_path, gitignore_my_stuff=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_my_stuff(shape)  # no my_stuff/ ignore
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_fails_when_required_and_absent(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "AG-18"})
        _write_project(tmp_path, gitignore_my_stuff=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_my_stuff(shape)
        assert f.tier == "fail"
        assert "my_stuff/" in f.message

    def test_passes_when_required_and_present(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01", "AG-18"})
        _write_project(tmp_path)  # .gitignore with my_stuff/ seeded by default
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_my_stuff(shape)
        assert f.tier == "ok"
        assert "git-ignored" in f.message


#: Real `.gitignore` shapes observed on the fleet, and whether each WITHHOLDS
#: `.claude/org/settings.json`. Every entry is a shape that actually existed.
_ORG_SHAPES: list[tuple[str, str, bool]] = [
    ("blanket dir deny (mgf-common-zig, pre-fix)", ".claude/org/\n", True),
    ("blanket dir deny, no slash", ".claude/org\n", True),
    (
        "deny-then-allowlist that never reaches org/ (soothfast, soothtroth)",
        ".claude/*\n!.claude/hooks/\n!.claude/settings.json\n",
        True,
    ),
    (
        "the no-op 'fix' — re-include without re-admitting the parent dir",
        ".claude/*\n!.claude/settings.json\n!.claude/org/settings.json\n",
        True,
    ),
    (
        "the real fix (inthewords) — re-admit, exclude contents, re-include policy",
        ".claude/*\n!.claude/settings.json\n!.claude/org/\n.claude/org/*\n"
        "!.claude/org/settings.json\n",
        False,
    ),
    (
        "canon's four-class shape (mgf-standard)",
        ".claude/settings.local.json\n.claude/org/*.local.json\n"
        ".claude/org/scheduled_tasks.lock\n.claude/org/old_*\n.claude/org/*.old\n",
        False,
    ),
    # Exercises the PARENT-EXCLUSION branch specifically: `.claude/` with no
    # wildcard excludes the whole tree, so nothing under org/ can be reached.
    # Without this shape a sabotage of that branch passed every other test —
    # the branch was not dead, it was untested, which reads identically from
    # a green run.
    ("bare .claude/ deny — the whole tree", ".claude/\n", True),
    # Verified against real git: STILL withheld. `.claude/` excludes the
    # directory itself, and re-admitting `org/` is not enough — the file's
    # grandparent is excluded, so git never descends to see the re-include.
    # `!.claude/` would have to come first. My expected value here was wrong
    # and the differential test caught it.
    (
        "bare .claude/ deny, org re-admitted but NOT .claude itself",
        ".claude/\n!.claude/org/\n.claude/org/*\n!.claude/org/settings.json\n",
        True,
    ),
    # Shapes a chair found diverging from real git. Each is verified against
    # `git check-ignore` by the differential test below, so they pin BEHAVIOUR,
    # not my belief about it.
    #
    # git does not strip LEADING whitespace from a rule, so this is not a rule:
    ("tab-indented — not a rule at all", "\t.claude/org/\n", False),
    # git does not accept a `./` prefix either:
    ("./ prefix — not a rule either", "./.claude/org/\n", False),
    # last match wins: a re-include that a later rule overrides is not a
    # re-admission. The first `_readmitted` scanned every line and ignored order.
    ("re-admit then re-exclude — order decides", "!.claude/\n/.claude/\n", True),
    ("no .claude rules at all", "my_stuff/\n.idea/\n", False),
    ("unrelated .claude rules only", ".claude/state/\n.claude/settings.local.json\n", False),
]


class TestOrgPolicyTravels:
    """AG-18(a) — `.claude/org/settings.json` MUST NOT be ignored.

    Enforced as a hard failure, unlike the four machine-local ignore lines
    which are distributed advisorily: when this shipped, **0 of 65** MGF-fleet
    repos withheld the file, so the prohibition costs nothing to obey. The
    failure it catches is silent by construction — a withheld policy file
    produces no error, no diff and no red gate.
    """

    _MOD = "mgf.fed.cli._doctor_standards._corpus_must_ids"

    @pytest.mark.parametrize(
        ("label", "body", "withholds"),
        [pytest.param(lbl, body, w, id=lbl) for lbl, body, w in _ORG_SHAPES],
    )
    def test_matcher_on_every_real_fleet_shape(
        self, label: str, body: str, withholds: bool
    ) -> None:
        assert _ignores_org_policy(body.splitlines()) is withholds, label

    def test_matches_real_git_on_every_shape(self, tmp_path) -> None:
        """Differential test: the git-free matcher vs `git check-ignore -q`.

        The matcher exists BECAUSE app-doctor runs on python:3.12-slim, which
        ships no git — so the authoritative implementation is unavailable
        exactly where the check must run. This pins the two together wherever
        git IS available. It also pins the verb: `-q`, never `-v`, because
        `check-ignore -v` exits 0 for NEGATION matches and would call a
        re-included file "ignored".
        """
        if not shutil.which("git"):
            pytest.skip("git not on PATH — the differential cannot run here")
        for i, (label, body, _) in enumerate(_ORG_SHAPES):
            repo = tmp_path / f"r{i}"
            repo.mkdir()
            subprocess.run(  # noqa: S603 — fixed git argv over a tmp_path fixture
                ["git", "init", "-q", str(repo)],  # noqa: S607
                check=True,
            )
            (repo / ".gitignore").write_text(body, encoding="utf-8")
            rc = subprocess.run(  # noqa: S603 — fixed git argv over a tmp_path fixture
                [  # noqa: S607
                    "git",
                    "-C",
                    str(repo),
                    "check-ignore",
                    "-q",
                    "--no-index",
                    ".claude/org/settings.json",
                ],
                capture_output=True,
            ).returncode
            git_says = rc == 0
            mine = _ignores_org_policy(body.splitlines())
            assert mine is git_says, f"{label}: matcher says {mine}, git says {git_says}"

    def test_inert_when_ag18_not_in_corpus(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"DP-01"})
        _write_project(tmp_path)
        (tmp_path / ".gitignore").write_text(".claude/org/\n", encoding="utf-8")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_org_policy_travels(shape)
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_fails_when_policy_is_withheld(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"AG-18"})
        _write_project(tmp_path)
        (tmp_path / ".gitignore").write_text(
            "my_stuff/\n.claude/*\n!.claude/settings.json\n", encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_org_policy_travels(shape)
        assert f.tier == "fail"
        # The message must name the actual mechanism, not just the symptom —
        # a bare `!.claude/org/settings.json` is a no-op and the operator
        # needs to know why before they "fix" it that way.
        assert "parent directory" in f.message
        assert "!.claude/org/" in f.message

    def test_passes_when_policy_travels(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: {"AG-18"})
        _write_project(tmp_path)
        (tmp_path / ".gitignore").write_text(
            "my_stuff/\n.claude/org/*.local.json\n.claude/org/scheduled_tasks.lock\n",
            encoding="utf-8",
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_org_policy_travels(shape)
        assert f.tier == "ok"

    def test_warn_when_corpus_unresolvable(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._MOD, lambda: None)
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_my_stuff(shape)
        assert f.tier == "unknown"


class TestCheckCommitHook:
    """AG-18 — the prepare-commit-msg hook gate. Self-gating: inert until the
    pinned corpus's AGENTS text references the hook (tightened AG-18, PAPER-102),
    so a project on an older corpus is unaffected."""

    _RS = "mgf.standard.read_standard"

    def test_reports_present_not_inert_when_git_is_unavailable(
        self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """No `git` on PATH (the slim CI test image) => the wiring is UNKNOWABLE, so
        the hook must read present-and-unchecked, NEVER "INERT" — that would be a
        false accusation on every containerised run (found in CI, PR #83)."""
        monkeypatch.setattr(self._RS, lambda name: "AG-18 … prepare-commit-msg …")
        monkeypatch.setattr("mgf.fed.cli._doctor_standards.shutil.which", lambda _name: None)
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "ok"
        assert "wiring not checked" in f.message
        assert "INERT" not in f.message

    def test_inert_when_corpus_lacks_hook_clause(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._RS, lambda name: "AG-18 wires .gitmessage via commit.template.")
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "ok"
        assert "not required" in f.message

    def test_fails_when_required_and_hook_absent(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._RS, lambda name: "AG-18 ... a prepare-commit-msg hook ...")
        _write_project(tmp_path, prepare_commit_hook=False)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)  # project has no .githooks/
        assert f.tier == "fail"
        assert "prepare-commit-msg" in f.message

    @staticmethod
    def _write_hook(tmp_path) -> None:
        hook = tmp_path / ".githooks" / "prepare-commit-msg"
        hook.parent.mkdir(parents=True, exist_ok=True)
        hook.write_text("#!/bin/sh\n", encoding="utf-8")

    @staticmethod
    def _git(tmp_path, *args: str) -> None:
        subprocess.run(  # noqa: S603 — fixed git argv over a tmp_path fixture
            ["git", "-C", str(tmp_path), *args],  # noqa: S607
            check=True,
            capture_output=True,
            text=True,
        )

    @pytest.mark.skipif(
        shutil.which("git") is None,
        reason="builds a real git repo fixture; the slim CI test image has no git "
        "(the probe itself degrades safely — see the no-git test above)",
    )
    def test_passes_when_hook_present_and_hookspath_wired(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._RS, lambda name: "AG-18 ... a prepare-commit-msg hook ...")
        _write_project(tmp_path)
        self._write_hook(tmp_path)
        self._git(tmp_path, "init", "-q")
        self._git(tmp_path, "config", "core.hooksPath", ".githooks")  # §4a wiring
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "ok"
        assert "active" in f.message

    @pytest.mark.skipif(
        shutil.which("git") is None,
        reason="builds a real git repo fixture; the slim CI test image has no git "
        "(the probe itself degrades safely — see the no-git test above)",
    )
    def test_passes_when_hook_present_and_shim_wired(self, tmp_path, monkeypatch) -> None:
        monkeypatch.setattr(self._RS, lambda name: "AG-18 ... a prepare-commit-msg hook ...")
        _write_project(tmp_path)
        self._write_hook(tmp_path)
        self._git(tmp_path, "init", "-q")
        shim = tmp_path / ".git" / "hooks" / "prepare-commit-msg"  # §4b pre-commit shim
        shim.parent.mkdir(parents=True, exist_ok=True)
        shim.write_text("#!/bin/sh\n", encoding="utf-8")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "ok"
        assert "active" in f.message

    @pytest.mark.skipif(
        shutil.which("git") is None,
        reason="builds a real git repo fixture; the slim CI test image has no git "
        "(the probe itself degrades safely — see the no-git test above)",
    )
    def test_warns_when_hook_present_but_inert(self, tmp_path, monkeypatch) -> None:
        """PAPER-117: committed hook + NO live wiring → advisory WARN, never FAIL."""
        monkeypatch.setattr(self._RS, lambda name: "AG-18 ... a prepare-commit-msg hook ...")
        _write_project(tmp_path)
        self._write_hook(tmp_path)
        self._git(tmp_path, "init", "-q")  # a repo, but neither §4a nor §4b wiring
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "warn"
        assert "INERT" in f.message
        assert "core.hooksPath" in f.message

    def test_warn_when_corpus_unreadable(self, tmp_path, monkeypatch) -> None:
        def _raise(name: str) -> str:
            raise FileNotFoundError(name)

        monkeypatch.setattr(self._RS, _raise)
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_commit_hook(shape)
        assert f.tier == "unknown"


class TestPackageRootFallback:
    """dist-name ≠ import-package (common for apps, e.g. `vm-vmanager-cli` →
    package `vmanager`): _discover_project must still find the single package
    under src/ rather than bailing with 'cannot detect shape'."""

    def test_single_package_when_name_does_not_derive_it(self, tmp_path) -> None:
        (tmp_path / "src" / "vmanager").mkdir(parents=True)
        (tmp_path / "src" / "vmanager" / "__init__.py").write_text('__version__ = "0.3.1"\n')
        (tmp_path / "src" / "vmanager" / "py.typed").touch()
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "vm-vmanager-cli"\nversion = "0.3.1"\n', encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_root.name == "vmanager"


class TestDeepNamespacePackageRoot:
    """Deep PEP-420 namespace (no __init__.py): mgf-vm-core → src/mgf/vm/core."""

    def test_deep_namespace_pep420(self, tmp_path) -> None:
        pkg = tmp_path / "src" / "mgf" / "vm" / "core"
        pkg.mkdir(parents=True)
        (pkg / "py.typed").touch()  # PEP-420: no __init__.py anywhere
        (pkg / "ids.py").write_text("X = 1\n")
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "mgf-vm-core"\nversion = "0.1.10"\n', encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_root == pkg

    def test_test_supervisor_not_misresolved_to_nested(self, tmp_path) -> None:
        # mgf-test-supervisor → src/mgf/test_supervisor, NOT src/mgf/test/supervisor.
        pkg = tmp_path / "src" / "mgf" / "test_supervisor"
        pkg.mkdir(parents=True)
        (pkg / "__init__.py").write_text("")
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "mgf-test-supervisor"\nversion = "0.1.11"\n', encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert shape.package_root == pkg


# ---------------------------------------------------------------------------
# TS-23 (CI coupling) — supervisor-under-coverage ↔ patch = ["subprocess"]
# (PAPER-122: green suite, red build)
# ---------------------------------------------------------------------------

_SUPERVISOR_PIPELINE = (
    "steps:\n"
    "  - name: test-3.12\n"
    "    commands:\n"
    "      - coverage run --source=src/mgf -m mgf.test_supervisor --tier 0-2\n"
    "      - coverage combine\n"
    "      - coverage report --fail-under=80\n"
)


class TestSupervisorCoveragePatch:
    def test_na_when_no_pipeline(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_supervisor_coverage_patch(shape).tier == "ok"

    def test_na_when_pipeline_does_not_run_the_supervisor_under_coverage(
        self, tmp_path: Path
    ) -> None:
        _write_project(tmp_path)
        (tmp_path / ".woodpecker.yml").write_text(
            "steps:\n  - name: test\n    commands:\n      - pytest -q\n", encoding="utf-8"
        )
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_supervisor_coverage_patch(shape)
        assert f.tier == "ok"
        assert "does not run" in f.message

    def test_fail_when_the_coupling_is_broken(self, tmp_path: Path) -> None:
        _write_project(tmp_path)  # no [tool.coverage.run] patch key
        (tmp_path / ".woodpecker.yml").write_text(_SUPERVISOR_PIPELINE, encoding="utf-8")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_supervisor_coverage_patch(shape)
        assert f.tier == "fail"
        assert f.rule_id == "TS-23"
        assert 'patch = ["subprocess"]' in f.message
        assert "every test passing" in f.message

    def test_ok_when_pyproject_carries_the_patch_key(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        (tmp_path / ".woodpecker.yml").write_text(_SUPERVISOR_PIPELINE, encoding="utf-8")
        with (tmp_path / "pyproject.toml").open("a", encoding="utf-8") as fh:
            fh.write('\n[tool.coverage.run]\npatch = ["subprocess"]\n')
        shape = _discover_project(tmp_path)
        assert shape is not None
        assert _check_supervisor_coverage_patch(shape).tier == "ok"

    def test_doctor_standards_exits_nonzero_on_the_broken_coupling(self, tmp_path: Path) -> None:
        _write_project(tmp_path)
        (tmp_path / ".woodpecker.yml").write_text(_SUPERVISOR_PIPELINE, encoding="utf-8")
        assert doctor_standards(root=tmp_path) == 2


class TestOrgPolicyDegradation:
    """ "I could not verify" MUST NOT share an exit code with "I verified"."""

    _MOD = "mgf.fed.cli._doctor_standards._corpus_must_ids"

    def test_unreadable_gitignore_warns_rather_than_passing(
        self, tmp_path: Path, monkeypatch
    ) -> None:
        """An unreadable `.gitignore` previously fell through to `lines=[]`
        and reported OK — indistinguishable from a repo with no rules, and a
        clean bill of health for a file nobody could read. A chair found it."""
        monkeypatch.setattr(self._MOD, lambda: {"AG-18"})
        _write_project(tmp_path)
        gi = tmp_path / ".gitignore"
        gi.unlink()
        # A DIRECTORY, not `chmod 0o000`. The first version chmod'd the file,
        # passed on the bench and FAILED in CI — which runs as ROOT, and root
        # reads a 0o000 file happily, so the read succeeded, the check saw a
        # real violation and correctly returned `fail`. The test was asserting
        # a property of uid 1000, not of the code. A directory raises
        # IsADirectoryError for every user, root included.
        gi.mkdir()
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_org_policy_travels(shape)
        assert f.tier == "unknown"
        assert "cannot verify" in f.message

    def test_empty_corpus_index_warns_rather_than_reporting_not_required(
        self, tmp_path: Path, monkeypatch
    ) -> None:
        """An Index of MUSTs that parses to an EMPTY set means the corpus could
        not be read, not that the rule is unrequired. The sibling check in this
        module guards this explicitly; the new one did not."""
        monkeypatch.setattr(self._MOD, lambda: set())
        _write_project(tmp_path)
        (tmp_path / ".gitignore").write_text(".claude/org/\n", encoding="utf-8")
        shape = _discover_project(tmp_path)
        assert shape is not None
        f = _check_gitignore_org_policy_travels(shape)
        assert f.tier == "unknown"


# --- "could not verify" must not share an exit code with "verified" ------------
#
# Board items 30, 31, 27. Six checks reported "cannot verify …" as `warn`, and
# `warn` returned 0 — so an audit that never ran reported the same exit code as
# an audit that passed. That is the exact failure this gate exists to end.


class TestCouldNotVerifyExitCode:
    def _shape(self, tmp_path: Path):
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "mgf-x"\n')
        (tmp_path / "src" / "mgf" / "x").mkdir(parents=True)
        return tmp_path

    def test_unknown_tier_exits_non_zero(
        self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
    ) -> None:
        root = self._shape(tmp_path)
        monkeypatch.setattr(
            _ds, "CHECKS", [lambda _s: _ds.Finding("X-01", "unknown", "cannot verify anything")]
        )
        rc = _ds.doctor_standards(root=root)
        assert rc != 0, "an audit that could not run reported success"
        assert "COULD NOT RUN" in capsys.readouterr().out

    def test_plain_warn_still_exits_zero(
        self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """An advisory whose check DID run stays advisory — this is not a
        blanket promotion of every warning to a failure."""
        root = self._shape(tmp_path)
        monkeypatch.setattr(
            _ds, "CHECKS", [lambda _s: _ds.Finding("X-01", "warn", "ran fine, just advising")]
        )
        assert _ds.doctor_standards(root=root) == 0

    def test_fail_still_wins_over_unknown(
        self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
    ) -> None:
        root = self._shape(tmp_path)
        monkeypatch.setattr(
            _ds,
            "CHECKS",
            [
                lambda _s: _ds.Finding("X-01", "unknown", "cannot verify"),
                lambda _s: _ds.Finding("X-02", "fail", "broken"),
            ],
        )
        assert _ds.doctor_standards(root=root) != 0
        assert "At least one rule failed" in capsys.readouterr().out


class TestGlobCharacterClasses:
    """Board item 27 — a false NEGATIVE on the one rule this gate enforces."""

    @pytest.mark.parametrize(
        "pattern",
        [".claude/or[g]/", ".claude/or[gG]/", ".claude/[o]rg/"],
    )
    def test_character_class_matches(self, pattern: str) -> None:
        assert _ds._pattern_hits(pattern, ".claude/org/settings.json") or _ds._pattern_hits(
            pattern.rstrip("/"), ".claude/org"
        ), f"{pattern} did not match — org policy could be ignored undetected"

    def test_negated_class_does_not_match(self) -> None:
        assert not _ds._pattern_hits(".claude/or[!g]/", ".claude/org")

    def test_unterminated_bracket_is_a_literal(self) -> None:
        """git treats an unterminated `[` literally; inventing a match here would
        be a false POSITIVE, which is its own defect.

        The target must be one that a WILDCARD would match and a literal would
        not — `.claude/oXrg`. An earlier version of this test used a target that
        failed for an unrelated reason (a trailing slash), so it passed whether
        the bracket was literal or a wildcard, and a sabotage of exactly this
        branch went undetected.
        """
        assert not _ds._pattern_hits(".claude/o[rg", ".claude/oXrg")
        assert _ds._pattern_hits(".claude/o[rg", ".claude/o[rg")

    def test_class_never_spans_a_slash(self) -> None:
        assert not _ds._pattern_hits("[a/b]", "a/b")


class TestNonUtf8Gitignore:
    """Board item 31 — UnicodeDecodeError is a ValueError, not an OSError, so it
    escaped the `except OSError` handler and crashed the whole audit."""

    def test_latin1_gitignore_does_not_crash_my_stuff_check(self, tmp_path: Path) -> None:
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "mgf-x"\n')
        (tmp_path / "src" / "mgf" / "x").mkdir(parents=True)
        (tmp_path / ".gitignore").write_bytes(b"my_stuff/\n# caf\xe9\n")
        shape = _ds._discover_project(tmp_path)
        assert shape is not None
        f = _ds._check_gitignore_my_stuff(shape)
        assert f.tier in {"ok", "warn", "unknown", "fail"}  # the point: it returned


class TestRepoWithoutPyproject:
    """Board item 29 — the gate could not reach its own violators.

    15 MGF-family repos have no `pyproject.toml` (mgf-brand, mgf-website-template,
    the Zig and asset repos, every sooth* repo). `_discover_project` returned None
    for all of them, so `app-doctor --standards` refused to run — and AG-18(a),
    the `.gitignore` org-policy rule, was UNENFORCEABLE in exactly the places most
    likely to violate it.
    """

    def test_a_git_repo_without_pyproject_still_yields_a_shape(self, tmp_path: Path) -> None:
        (tmp_path / ".git").mkdir()
        shape = _ds._discover_project(tmp_path)
        assert shape is not None, "the gate cannot run on a repo with no pyproject"
        assert shape.root == tmp_path
        assert shape.package_root is None  # package checks degrade to N/A, as they already do

    def test_a_directory_that_is_not_a_repo_still_returns_none(self, tmp_path: Path) -> None:
        """Widening must not mean auditing any random directory."""
        assert _ds._discover_project(tmp_path) is None

    def test_root_level_checks_actually_run_there(self, tmp_path: Path) -> None:
        (tmp_path / ".git").mkdir()
        (tmp_path / ".gitignore").write_text(".claude/org/settings.json\n")
        shape = _ds._discover_project(tmp_path)
        assert shape is not None
        f = _ds._check_gitignore_org_policy_travels(shape)
        assert f.tier in {"fail", "ok", "unknown"}  # it RAN; that is the finding

    def test_pytest_config_is_not_demanded_of_a_repo_with_no_pyproject(
        self, tmp_path: Path
    ) -> None:
        """Noise from a newly widened gate is how the widening gets reverted."""
        (tmp_path / ".git").mkdir()
        shape = _ds._discover_project(tmp_path)
        assert shape is not None
        assert _ds._check_filterwarnings(shape).tier == "ok"


class TestPatternHitsBranches:
    """Board item 28 — branches in the matcher that no shape pinned.

    Each row here is one branch. Removing any of them now fails a test, which is
    the point: an unpinned branch is indistinguishable from dead code, and the
    only way anyone learns which is by deleting it and seeing what breaks.
    """

    @pytest.mark.parametrize(
        ("pat", "target", "hits"),
        [
            (".claude/org/settings.json", ".claude/org/settings.json", True),  # exact
            ("/.claude/org", ".claude/org", True),  # leading slash stripped
            ("settings.json", ".claude/org/settings.json", True),  # basename, any depth
            (".claude/*", ".claude/org", True),  # one level
            (".claude/*", ".claude/org/settings.json", False),  # * does NOT cross /
            (".claude/**", ".claude/org/settings.json", True),  # ** does
            (".claude/or?", ".claude/org", True),  # single char
            (".claude/or?", ".claude/or/g", False),  # ? does not cross / either
            ("nothing", ".claude/org", False),
        ],
    )
    def test_branch(self, pat: str, target: str, hits: bool) -> None:
        assert _ds._pattern_hits(pat, target) is hits, f"{pat!r} vs {target!r}"
