"""A record is cited by id AND title — never by number alone.

THE PROBLEM THIS IS ABOUT. The agent has the record open; the operator does
not. `#3244` reads as complete to the writer and as homework to the reader,
who has to look it up to know what their own conversation is about. The
operator's words: *"I don't know what a note, task, or milestone is by its ID
number."*

PRODUCT, NOT A RULE (rule 119). Every Scribe user hits this, so the fix is in
the surfaces the product ships — the skill that shapes how an agent writes,
and the tool responses that hand a record back. A per-instance rule would fix
it for one operator and leave the behaviour wrong for everyone else.

Scribe's duplicate gate already had the right shape — `id 412: "debounce
helper"` — which is why these assert the CONVENTION reaches the other
surfaces rather than inventing a new one.
"""
import pathlib

import pytest
from unittest.mock import AsyncMock, MagicMock, patch

SKILL = (
    pathlib.Path(__file__).resolve().parents[1]
    / "plugin/skills/using-scribe/SKILL.md"
)


def test_the_skill_carries_the_convention():
    """The skill is read while deciding HOW to write, which is the only moment
    this can be applied. A tool response can name one record; only the skill
    can govern the prose around it."""
    text = " ".join(SKILL.read_text().split())
    assert "Name the record, never just its number" in text, (
        "the using-scribe skill no longer tells an agent to write the title "
        "alongside the id. Nothing else governs how records are cited in "
        "prose, commit messages, or task bodies."
    )


def test_the_skill_says_where_it_matters_most():
    """A convention stated only for chat messages gets applied only there —
    and the places read LATER, by someone with even less context, are where a
    bare id costs most."""
    text = " ".join(SKILL.read_text().split()).lower()
    assert "commit message" in text and "task bod" in text


def test_the_skill_says_to_look_up_a_title_it_does_not_know():
    """The escape hatch that would otherwise swallow the convention whole: an
    agent that does not know the title will emit the number and move on."""
    text = " ".join(SKILL.read_text().split()).lower()
    assert "an id you can't name is one you haven't checked" in text


# ── The tool responses (the other half) ────────────────────────────────
#
# A deletion is the sharpest case: afterwards the row is trashed, so if the
# confirmation did not name it, nothing can. An operator who cannot recognise
# what was deleted cannot tell it was the wrong thing.

@pytest.mark.parametrize("module,fn,kind,rid", [
    ("tasks", "delete_task", "task", 3244),
    ("notes", "delete_note", "note", 2109),
    ("milestones", "delete_milestone", "milestone", 323),
])
@pytest.mark.asyncio
async def test_a_delete_confirmation_names_what_it_deleted(
    module, fn, kind, rid, monkeypatch,
):
    import importlib

    mod = importlib.import_module(f"scribe.mcp.tools.{module}")
    title = "the staleness signal"
    row = MagicMock()
    row.title = title

    patches = [
        patch.object(mod, "current_user_id", MagicMock(return_value=1)),
        patch.object(mod.trash_svc, "delete", AsyncMock(return_value="batch-1")),
    ]
    if module == "milestones":
        patches.append(
            patch.object(mod.milestones_svc, "get_milestone",
                         AsyncMock(return_value=row)))
    else:
        patches.append(
            patch.object(mod.notes_svc, "get_note_for_user",
                         AsyncMock(return_value=(row, "owner"))))
    for p in patches:
        p.start()
    try:
        out = await getattr(mod, fn)(rid)
    finally:
        for p in patches:
            p.stop()

    assert out["title"] == title, f"{fn} returns no title for the record"
    assert title in out["message"], (
        f"{fn}'s message names only the id. After the delete the row is "
        f"trashed, so this line is the last chance to say WHAT went."
    )
    assert str(rid) in out["message"], (
        f"{fn} dropped the id — the title alone is not addressable, and the "
        f"convention is id AND title, not one or the other."
    )
