"""Unit: locked persona prompt builder (PLAN §6 verbatim + both modes).""" from __future__ import annotations import uuid import pytest from app.models import Document from app.rag.prompts import PERSONA, _base, build_deflect_prompt, build_high_prompt def _doc(path: str, content: str, title: str) -> Document: return Document( id=uuid.uuid4(), source="Homelab", path=path, full_path=f"/tmp/{path}", title=title, content=content, content_hash="0" * 64, ) def test_persona_rules_present_verbatim() -> None: # Aligned to the owner's working-tree persona edits (PLAN §6 revision, # 2026-08-22): no "you've got this" tagline, no mandated deflection # opening. The honesty gate itself (rule 3) is unchanged. for fragment in ( 'You are "Brain of Reese" — the digital brain of Reese, a self-hoster and', "optimistic about the user's ability to do things", "Answer ONLY from the provided document context. Cite which document(s)", "you used, by path.", "Be concrete: names, versions, ports, hosts, schedules", 'HONESTY GATE: if is "LOW", you must NOT pretend to know', "Offer 2-3 alternative questions about things you DO have notes on.", "Never invent facts, hosts, or steps that are not in the context.", "Keep answers tight: short paragraphs, bullets where helpful.", ): assert fragment in PERSONA def test_persona_owner_edits_are_preserved() -> None: """PLAN §6 revision (2026-08-22): the removed elements must stay out.""" assert 'you\'ve got this' not in PERSONA # tagline removed by the owner assert "Start your answer with a variant of" not in PERSONA # no mandated opening assert "HONESTY GATE" in PERSONA # the gate itself is intact def test_high_prompt_carries_relevance_marker_and_full_documents() -> None: doc = _doc("kubernetes.md", "Talos Linux on three nodes.", "Kubernetes Homelab Cluster") prompt = build_high_prompt([doc]) assert "HIGH" in prompt assert "DEFLECT_MODE" not in prompt assert "" in prompt and "" in prompt assert 'path="kubernetes.md"' in prompt assert "Talos Linux on three nodes." in prompt assert "HONESTY GATE" in prompt # persona intact def test_high_prompt_lists_multiple_documents_in_order() -> None: a = _doc("a.md", "CONTENT_A", "Title A") b = _doc("b.md", "CONTENT_B", "Title B") prompt = build_high_prompt([a, b]) assert prompt.index("CONTENT_A") < prompt.index("CONTENT_B") assert 'title="Title B"' in prompt def test_high_prompt_without_documents_stays_honest() -> None: prompt = build_high_prompt([]) assert "" in prompt assert "do not invent specifics" in prompt def test_low_prompt_has_deflect_mode_and_titles_only() -> None: titles = ["Kubernetes Homelab Cluster", "Backup Strategy"] prompt = build_deflect_prompt(titles) assert "LOW" in prompt assert "DEFLECT_MODE" in prompt # marker the E2E mock keys on assert "- Kubernetes Homelab Cluster" in prompt assert "- Backup Strategy" in prompt def test_low_prompt_never_contains_document_content() -> None: secret = "SECRET_DOCUMENT_CONTENT_12345" prompt = build_deflect_prompt(["Some Title"]) assert secret not in prompt assert "" not in prompt assert "HONESTY GATE" in prompt # the LOW rule is what the model must follow def test_low_prompt_with_no_titles() -> None: assert "nothing close at all" in build_deflect_prompt([]) def test_zero_note_prompt_is_byte_identical_to_pre_steering() -> None: """Phase 15 contract: with no steering notes the prompt is exactly what it was before the section existed.""" doc = _doc("kubernetes.md", "Talos Linux on three nodes.", "Kubernetes Homelab Cluster") block = ( '\n' "Talos Linux on three nodes.\n" "" ) assert build_high_prompt([doc]) == _base("HIGH") + "\n\n" + block + "\n" assert build_deflect_prompt(["T1", "T2"]) == ( _base("LOW") + "\nDEFLECT_MODE: retrieval was weak — the titles below are the closest " "your notes come to the question. They are titles only; do not pretend " "they answer it. Use them to propose 2-3 alternative questions.\n" + "- T1\n- T2" ) assert "" not in build_high_prompt([doc]) assert "" not in build_deflect_prompt([]) def test_relevance_placeholder_rejected_for_garbage() -> None: with pytest.raises(ValueError, match="HIGH or LOW"): _base("MEDIUM")