**Phase 182 — final verification pass (all 8 tasks already complete; this was verification + docs + one defect fix).**
- Verified: `app/rag/verbatim.py` (locator grammar, `splitlines()` slice, line-boundary cap, `resolve_answer_blocks`), `SHOW_TEXT_TOOL` + `shown_texts`, `SHOW_TEXT_CLAUSE`, `done.answer_text` (9 fields, key dropped when empty) + `ChatMessageRecord.answer_text` (≤50, 600 000-char cap counted in the 8 M budget), `verbatim.js` + 4 `{verbatim:true}` renderers, `Containerfile` minify step, `.env.example` 3 vars.
- `uv run python -c "…answer_text, show_text_max_chars, show_text_max_blocks"` → `True 12000 5`; each negative refuses startup naming its field; `0` accepted as inert ✅
- `bash .agents/validate.sh` → **validation OK**: 4320 unit+integration tests green, `TOTAL 6849 17 → 99%` (gate app/ >90% OK), `ruff check .` "All checks passed!", `pyright` 0 errors (re-run after my edits, 08:28).
- E2E, each in isolation `--no-cov`: `test_verbatim_show_text.py` 13 passed (panel bytes == `documents.content`, server caption, clipboard == server string, `/document.html?source=…&path=…`, honest unresolved state, reload+share, `show_image` regression, flag-off app) ✅; green: `test_answer_images` `test_harness_aligned_tools` `test_agent_document_tools` `test_markdown_tables` `test_markdown_inline_links_images` `test_svg_document_preview` `test_smoke` `test_document_viewer` `test_markdown_blockquotes` `test_chat_persistence` `test_thinking_timeline` `test_chat_rag` `test_settings_mobile_value_width`.
- **Defect found & fixed (pre-existing, not phase 182):** `tests/e2e/test_admin_settings_panel.py::test_admin_sees_groups_and_source_badges` failed — its whole-page `assert "reese" not in page.content()` tripped on the shell's onboarding chips, which follow the newest saved chats in the one shared E2E DB (a leftover `reeselink.json` question from a tool suite). Scoped the strict sweep to `.settings-shell` and kept a whole-page `reese:reese` credential needle → 12 passed with the leftovers still present.
- Docs: README fixed 2 inconsistencies — Quick Start omitted the **required** `BOR_ADMIN_PASSWORD`/`BOR_SESSION_SECRET` (the app fail-loud refuses boot) and the broken anchor `#chat--` → `#chat-`; full re-read of README (1816 lines) + AGENTS confirmed the phase-182 feature/settings/log/UI-contract text matches the code, and every `.env.example` var + every `Settings` field is documented.
- Notable: kept `resolve_answer_blocks` synchronous on the SSE terminal path (bounded indexed SELECTs, matching the existing `query_log` precedent and the phase's documented fence-lookup bound); no live-infra changes made.
- Next pending phase: none — `todo/` holds only 182 (its 8 task files are in `complete/`; the harness commits + moves `00_phase.md`).
376 lines
18 KiB
Python
376 lines
18 KiB
Python
"""Prompt-lock pin (phase 112, task 03 — owner decision iii, 2026-09-14).
|
||
|
||
The persona + HONESTY GATE text is **locked verbatim** (PLAN §6): it
|
||
changes through the plan, never in code. This module byte-pins the
|
||
locked prompt constants against their pre-phase-112 anchor values —
|
||
sha256 + exact prefix/suffix + total length, so *any* byte change
|
||
(option (i)'s copy tightening, option (ii)'s plan amendment, or an
|
||
accidental edit) fails loudly until the anchors are re-cut as part of
|
||
the same plan revision. ``tests.unit.test_prompts`` pins the
|
||
assembled-prompt structure and the behavioral contracts on top of
|
||
these constants; this file pins the constants themselves.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
|
||
from app.rag.prompts import (
|
||
ORIGIN_RULE,
|
||
PERSONA,
|
||
SEARCH_DISCIPLINE_RULE,
|
||
SHOW_IMAGE_CLAUSE,
|
||
SHOW_TEXT_CLAUSE,
|
||
SUGGEST_INTRO,
|
||
TOOLS_SECTION,
|
||
_base,
|
||
build_deflect_prompt,
|
||
build_high_prompt,
|
||
)
|
||
|
||
|
||
def _sha256(text: str) -> str:
|
||
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
||
|
||
|
||
# ---------- PERSONA (the locked base of BOTH the HIGH and LOW prompts) ----------
|
||
|
||
#: Pre-phase-112 anchors for ``PERSONA`` (the ``{relevance}`` placeholder
|
||
#: and the line wrapping included).
|
||
PERSONA_SHA256 = "e31792a73e64c53853097e0f7b6df8b96c5f2d05c286944c3edba16dde7777fe"
|
||
PERSONA_LEN = 706
|
||
PERSONA_PREFIX = (
|
||
'You are "Brain of Reese" — the digital brain of Reese, a self-hoster and\n'
|
||
"homelab tinkerer. Personality: chippy, upbeat, warm, and genuinely\n"
|
||
"optimistic about the user's ability to do things.\n"
|
||
"\n"
|
||
"Rules:\n"
|
||
)
|
||
PERSONA_SUFFIX = (
|
||
"4. Never invent facts, hosts, or steps that are not in the context.\n"
|
||
"5. Keep answers tight: short paragraphs, bullets where helpful.\n"
|
||
"\n"
|
||
"<relevance>{relevance}</relevance>"
|
||
)
|
||
|
||
|
||
def test_persona_byte_locked() -> None:
|
||
"""Any byte change to the locked persona (opening, any rule line, the
|
||
``<relevance>`` placeholder) fails on the sha256; the prefix/suffix
|
||
anchors name the damaged region for the diff."""
|
||
assert len(PERSONA) == PERSONA_LEN
|
||
assert _sha256(PERSONA) == PERSONA_SHA256
|
||
assert PERSONA.startswith(PERSONA_PREFIX)
|
||
assert PERSONA.endswith(PERSONA_SUFFIX)
|
||
|
||
|
||
def test_high_and_low_bases_byte_locked() -> None:
|
||
"""``_base`` only substitutes ``{relevance}`` — the per-mode base
|
||
lengths pin the substitution against a moved or re-spelled
|
||
placeholder in the locked text."""
|
||
assert len(_base("HIGH")) == 699 # PERSONA_LEN - 11 + 4
|
||
assert len(_base("LOW")) == 698 # PERSONA_LEN - 11 + 3
|
||
|
||
|
||
# ---------- TOOLS_SECTION (the HIGH prompt's locked ``<tools>`` copy) ----------
|
||
|
||
#: Anchors for ``TOOLS_SECTION`` — pre-phase-112 values, RE-CUT for
|
||
#: phase 118 (task 04, A6, owner directive 2026-09-15): the ``read``
|
||
#: clause was rewritten for the summary-seed mode (the ``<documents>``
|
||
#: section holds SUMMARIES — ``read`` adds the full text; only an
|
||
#: already-read document is refused). RE-CUT again for phase 141
|
||
#: (task 02): the re-read sentence was re-cut to the coverage-true
|
||
#: form — do not re-read a document read IN FULL (answer from it);
|
||
#: a PARTIAL document (a range read, or a truncated read) is not
|
||
#: complete in the context until read all — read the other parts with
|
||
#: ``offset``/``limit``. RE-CUT again for phase 145 (task 02): the
|
||
#: ``read`` clause gains the original-source rule — :data:`ORIGIN_RULE`,
|
||
#: the one sentence-group appended at the ``read`` clause's end (the
|
||
#: truncation workflow sentence) and BEFORE the ``grep`` clause begins
|
||
#: (the ``origin:`` line is a read-result fact; the rule binds the
|
||
#: ANSWER that uses what the read added). Only that sentence-group was
|
||
#: inserted — the prefix (the ``ls``-clause opening) and the suffix
|
||
#: (the discipline-rules ending) survived byte-identical, so they are
|
||
#: the same anchors as pre-phase-145; the pre-phase-145 sha/length are
|
||
#: kept below for the chained diff-style pin. RE-CUT again for phase 162
|
||
#: (task 03, LOCKED A3+A4): the ``grep`` sentence's ``(case-insensitive)``
|
||
#: parenthetical widened to the folded contract ``(case- and
|
||
#: accent-insensitive — accents fold: besta matches BESTÅ)``, and the
|
||
#: LOCKED search-discipline sentence (:data:`SEARCH_DISCIPLINE_RULE`) was
|
||
#: inserted immediately after the grep sentence (before the ``For a normal
|
||
#: search pass`` clause). Only those two byte-groups moved — the prefix
|
||
#: (the ``ls``-clause opening) and the suffix (the discipline-rules
|
||
#: ending) survive byte-identical; the pre-phase-162 sha/length are kept
|
||
#: below for the chained diff-style pin.
|
||
TOOLS_SECTION_SHA256 = "988642f9dc6bb2a4a1d44a83b5368ff4cdb9df4e181b2fe3dc2fb241b1857595"
|
||
TOOLS_SECTION_LEN = 3818
|
||
|
||
# The pre-phase-162 anchors — the section BEFORE phase 162's grep-fold +
|
||
# search-discipline insertion. Kept for the chained diff-style pin.
|
||
TOOLS_SECTION_PRE_PHASE_162_SHA256 = (
|
||
"41a84885be98d4e15b95205694d59d364296a30e27758d39ae737881d5bacaa7"
|
||
)
|
||
TOOLS_SECTION_PRE_PHASE_162_LEN = 3279
|
||
|
||
# The grep contract parenthetical exactly as it changed in phase 162
|
||
# (LOCKED A3): the pre-phase ``(case-insensitive)`` vs the folded one now
|
||
# embedded in ``TOOLS_SECTION``. Used to revert phase 162 for the diff pin.
|
||
_GREP_CONTRACT_PRE_PHASE_162 = "(case-insensitive)"
|
||
_GREP_CONTRACT_PHASE_162 = (
|
||
"(case- and accent-insensitive — accents fold: `besta` matches `BESTÅ`)"
|
||
)
|
||
|
||
TOOLS_SECTION_PREFIX = (
|
||
"<tools>\n"
|
||
"You may extend your context with three tools. `ls` lists the "
|
||
"knowledge base as a tree, one level at a time: "
|
||
)
|
||
TOOLS_SECTION_SUFFIX = (
|
||
"Never repeat a call that was refused or already succeeded — the refusal "
|
||
"already told you the correct form. Answer as soon as you have what "
|
||
"you need.\n</tools>"
|
||
)
|
||
|
||
#: The pre-phase-145 anchors — the section BEFORE :data:`ORIGIN_RULE` was
|
||
#: inserted. Kept for the diff-style pin: the ONLY byte change is the
|
||
#: rule's insertion, so removing the rule reproduces these exact bytes.
|
||
TOOLS_SECTION_PRE_PHASE_145_SHA256 = (
|
||
"3671680bbf4e04efe1186690c18d24869e4974ceb29485cedc3e34a9286bf3b2"
|
||
)
|
||
TOOLS_SECTION_PRE_PHASE_145_LEN = 2656
|
||
|
||
|
||
#: Phase 145 (task 02) — the original-source rule the ``read`` teaching
|
||
#: carries is :data:`app.rag.prompts.ORIGIN_RULE` (imported above, LOCKED
|
||
#: verbatim). :data:`TOOLS_SECTION` embeds that exact sentence-group at
|
||
#: the ``read`` clause's end (the truncation workflow sentence) and
|
||
#: BEFORE the ``grep`` clause begins. A read result for a git-sourced
|
||
#: document carries an ``origin:`` line (and a ``branch:`` line when a
|
||
#: branch is pinned) — the read contract's fact — so when the answer uses
|
||
#: such a document it includes the original source in the answer: a link
|
||
#: to the repository (the ``origin`` line) with the document's path (the
|
||
#: ``Document …`` header) named and the pinned branch when one is shown,
|
||
#: as the repository's file-view URL where the model can tell it
|
||
#: (``<repo-url>/blob/<branch>/<path>``), or the repository URL plus the
|
||
#: path as plain text otherwise. The rule is CONDITIONAL on the ``origin``
|
||
#: line's presence — a local-sourced document's read result carries none,
|
||
#: so the rule adds nothing to those answers (the model has no URL to
|
||
#: cite; it must not invent one — the sentence's conditioning does that
|
||
#: job).
|
||
|
||
|
||
def test_tools_section_byte_locked() -> None:
|
||
"""The ``<tools>`` teaching is LOCKED verbatim too (the E2E mock keys
|
||
on the ``<tools>`` marker's presence; the wording is the owner's):
|
||
sha256 + exact prefix/suffix + total length."""
|
||
assert len(TOOLS_SECTION) == TOOLS_SECTION_LEN
|
||
assert _sha256(TOOLS_SECTION) == TOOLS_SECTION_SHA256
|
||
assert TOOLS_SECTION.startswith(TOOLS_SECTION_PREFIX)
|
||
assert TOOLS_SECTION.endswith(TOOLS_SECTION_SUFFIX)
|
||
|
||
|
||
def test_tools_section_origin_rule_verbatim_at_its_position() -> None:
|
||
"""Phase 145 (task 02): the original-source rule is LOCKED verbatim
|
||
and sits at its EXACT position — the ``read`` clause's end (the
|
||
truncation workflow sentence) and BEFORE the ``grep`` clause begins.
|
||
The sentence-group is pinned byte-for-byte, and the surrounding
|
||
boundary (the truncation sentence's tail → the rule → the ``grep``
|
||
clause's opening) is pinned as one contiguous substring, so a move
|
||
or re-spelling of ANY byte (the rule's or the boundary's) fails."""
|
||
# Verbatim presence.
|
||
assert ORIGIN_RULE in TOOLS_SECTION
|
||
assert TOOLS_SECTION.count(ORIGIN_RULE) == 1 # exactly once
|
||
# Exact position: after the truncation sentence, before the grep
|
||
# clause — one contiguous span, no drift at either seam.
|
||
assert (
|
||
"it searches the whole document. "
|
||
+ ORIGIN_RULE
|
||
+ " `grep` locates an exact string"
|
||
) in TOOLS_SECTION
|
||
|
||
|
||
def test_tools_section_search_discipline_rule_verbatim_at_its_position() -> None:
|
||
"""Phase 162 (task 03, LOCKED A4): the search-discipline rule is
|
||
LOCKED verbatim and sits at its EXACT position — immediately after
|
||
the grep sentence (the ``…retry with the plain text you expect to
|
||
see.`` clause) and BEFORE the ``For a normal search pass`` clause.
|
||
The rule + its exact boundaries are pinned as one contiguous span, so
|
||
a move or re-spelling of ANY byte fails."""
|
||
assert SEARCH_DISCIPLINE_RULE in TOOLS_SECTION
|
||
assert TOOLS_SECTION.count(SEARCH_DISCIPLINE_RULE) == 1 # exactly once
|
||
assert (
|
||
"retry with the plain text you expect to see. "
|
||
+ SEARCH_DISCIPLINE_RULE
|
||
+ " For a normal search pass only `pattern`"
|
||
) in TOOLS_SECTION
|
||
|
||
|
||
def test_tools_section_grep_fold_contract_present() -> None:
|
||
"""Phase 162 (task 03, LOCKED A3): the grep sentence now states the
|
||
FOLDED contract — the ``(case-insensitive)`` parenthetical is replaced
|
||
byte-for-byte by the folded one, and the old one is gone."""
|
||
assert _GREP_CONTRACT_PHASE_162 in TOOLS_SECTION
|
||
assert _GREP_CONTRACT_PRE_PHASE_162 not in TOOLS_SECTION
|
||
|
||
|
||
def test_tools_section_phase162_and_145_are_the_only_byte_changes() -> None:
|
||
"""Chained diff-style pin: the ONLY byte changes are phase 162's (the
|
||
folded grep contract + :data:`SEARCH_DISCIPLINE_RULE`) and phase 145's
|
||
(:data:`ORIGIN_RULE`). Reverting phase 162 (drop the rule + its
|
||
separating space, restore the pre-phase grep parenthetical) reproduces
|
||
the pre-phase-162 section byte-for-byte; reverting the origin rule on
|
||
top of that reproduces the pre-phase-145 section byte-for-byte."""
|
||
# Revert phase 162 -> pre-phase-162.
|
||
pre_162 = TOOLS_SECTION.replace(SEARCH_DISCIPLINE_RULE + " ", "")
|
||
pre_162 = pre_162.replace(_GREP_CONTRACT_PHASE_162, _GREP_CONTRACT_PRE_PHASE_162)
|
||
assert len(pre_162) == TOOLS_SECTION_PRE_PHASE_162_LEN
|
||
assert _sha256(pre_162) == TOOLS_SECTION_PRE_PHASE_162_SHA256
|
||
# Revert phase 145 -> pre-phase-145.
|
||
pre_145 = pre_162.replace(ORIGIN_RULE + " ", "")
|
||
assert len(pre_145) == TOOLS_SECTION_PRE_PHASE_145_LEN
|
||
assert _sha256(pre_145) == TOOLS_SECTION_PRE_PHASE_145_SHA256
|
||
|
||
|
||
# ---------- the LOW prompt's locked DEFLECT_MODE body ----------
|
||
|
||
#: The ``DEFLECT_MODE`` body exactly as it was pre-phase-112 (the
|
||
#: phase-71 plain-text line included) — inline in
|
||
#: :func:`app.rag.prompts.build_deflect_prompt`, so it is pinned through
|
||
#: the built prompt rather than a module constant.
|
||
LOW_BODY_SHA256 = "c9868cfccdd0ff79d7c1de5df5f6dea0182d3726ca912c9ef609ab54b563a0a4"
|
||
LOW_BODY_LEN = 259
|
||
LOW_BODY = (
|
||
"DEFLECT_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"
|
||
"Reply in plain text only — you have no tools in this mode."
|
||
)
|
||
|
||
|
||
def test_deflect_body_byte_locked() -> None:
|
||
"""The LOW build = locked base + exactly the locked DEFLECT_MODE body
|
||
+ the weak-hit title list — byte for byte (the mock keys on the
|
||
``DEFLECT_MODE`` marker's presence; the body wording is locked)."""
|
||
assert len(LOW_BODY) == LOW_BODY_LEN
|
||
assert _sha256(LOW_BODY) == LOW_BODY_SHA256
|
||
assert build_deflect_prompt(["T1", "T2"]) == _base("LOW") + "\n" + LOW_BODY + "\n- T1\n- T2"
|
||
prompt = build_deflect_prompt(["T1", "T2"])
|
||
assert prompt.count(LOW_BODY) == 1
|
||
assert prompt.index("DEFLECT_MODE") < prompt.index(
|
||
"Reply in plain text only"
|
||
) # the marker precedes the plain-text line
|
||
|
||
|
||
#: The full LOW prompt build on the canonical fixture titles — sha-pinned
|
||
#: (phase 118, task 03): the deflection path is UNTOUCHED by the
|
||
#: summary-seed re-revision (LOCKED A8) — the same inputs must produce
|
||
#: the pre-phase bytes, so this anchor is a pre-phase-118 value.
|
||
LOW_PROMPT_SHA256 = "726eddb4eb3bcc26c840011f6f8635d6af55aa09d4d064bbb9e256caa9665837"
|
||
LOW_PROMPT_LEN = 968
|
||
|
||
|
||
def test_low_prompt_build_byte_identical_to_pre_phase() -> None:
|
||
"""Phase 118 contract: the LOW prompt output is byte-identical to
|
||
pre-phase for identical inputs — the summary seeding (and the new
|
||
``SUGGEST_INTRO`` line) never leaks into the deflection path."""
|
||
prompt = build_deflect_prompt(["T1", "T2"])
|
||
assert len(prompt) == LOW_PROMPT_LEN
|
||
assert _sha256(prompt) == LOW_PROMPT_SHA256
|
||
assert "SUGGEST_INTRO" not in prompt and "<documents>" not in prompt
|
||
|
||
|
||
# ---------- SUGGEST_INTRO (phase 118 framing, re-cut for phase 119, LOCKED A5) ----------
|
||
|
||
#: Anchors for ``SUGGEST_INTRO`` — the ``<documents>`` section's intro
|
||
#: line (the owner's "start here if these summaries seem right to you"
|
||
#: framing, TODO L3, phase 118). RE-CUT for phase 119 (task 04, LOCKED
|
||
#: A5, owner directive 2026-09-16): the final sentence was replaced
|
||
#: with the cite-discipline sentence (cite only the documents read — or
|
||
#: the suggested document answered from without reading — never a
|
||
#: document neither read nor used), closing the live confabulation in
|
||
#: which the model cited a file it never read; the phase-118 "cite the
|
||
#: document(s) you used, by path" sentence is retired. Only that
|
||
#: sentence moved — the start-here framing, the ``read`` pointer, and
|
||
#: the full-text clause survive byte-identical, so the prefix anchor is
|
||
#: the same as pre-phase-119. The E2E mock's ``_document_block`` parser
|
||
#: is regex-based over the block markup (which stays byte-stable around
|
||
#: the intro), so this constant is a prompt-copy lock, pinned the way
|
||
#: ``TOOLS_SECTION`` is: sha256 + prefix + total length. ``PERSONA``,
|
||
#: ``TOOLS_SECTION``, and the LOW body anchors above stay byte-identical
|
||
#: — this change touches no other constant.
|
||
SUGGEST_INTRO_SHA256 = "c6ffbb19975cbf184910a9a8e8fdea101c16f5d3eecb872fb07afeabd2763302"
|
||
SUGGEST_INTRO_LEN = 460
|
||
SUGGEST_INTRO_PREFIX = (
|
||
"The blocks below are the summaries of the top-ranked documents for "
|
||
"your question — start here if one seems right to you: "
|
||
)
|
||
|
||
|
||
def test_suggest_intro_byte_locked() -> None:
|
||
"""The start-here framing is LOCKED copy (phase 118; the final
|
||
sentence re-cut by phase 119, LOCKED A5): sha256 + exact prefix +
|
||
total length; the four contracts it must carry (summaries are the
|
||
starting points; ``read`` adds the full text, which is NOT in the
|
||
prompt until read; the phase-119 cite discipline — cite only what
|
||
was read, or the suggested document answered from without reading,
|
||
never a document neither read nor used) are pinned as substrings."""
|
||
assert len(SUGGEST_INTRO) == SUGGEST_INTRO_LEN
|
||
assert _sha256(SUGGEST_INTRO) == SUGGEST_INTRO_SHA256
|
||
assert SUGGEST_INTRO.startswith(SUGGEST_INTRO_PREFIX)
|
||
assert "call `read" in SUGGEST_INTRO
|
||
assert "combined `source/path`" in SUGGEST_INTRO
|
||
assert "its full text is not in the prompt until you read it" in SUGGEST_INTRO
|
||
assert (
|
||
"Cite only the document(s) you read — or, if you answered from a "
|
||
"suggested summary without reading it, cite that suggested "
|
||
"document — never a document you neither read nor used."
|
||
) in SUGGEST_INTRO
|
||
# The retired phase-118 final sentence is gone from the constant.
|
||
assert "Cite the document(s) you used, by path." not in SUGGEST_INTRO
|
||
|
||
|
||
# ---------- SHOW_TEXT_CLAUSE (phase 182, task 04: verbatim display) ----------
|
||
|
||
#: Anchors for ``SHOW_TEXT_CLAUSE`` — the ``show_text`` / ```bor``` locator
|
||
#: teaching appended INSIDE ``<tools>`` when ``BOR_ANSWER_TEXT`` is on
|
||
#: (phase 182, LOCKED A1–A3/A6). Pinned the way ``TOOLS_SECTION`` is
|
||
#: (sha256 + exact prefix + total length) because this clause IS the fix
|
||
#: for the retype bug the phase exists for: a silent wording drift here
|
||
#: re-opens it (the tool alone never teaches that the user copies from
|
||
#: what is DISPLAYED, nor the fence syntax).
|
||
SHOW_TEXT_CLAUSE_SHA256 = "e03a8153ca6cde61c9437f3b66b4fa07cd2a3f9f2e24e3bb8c37ca1d7c7e9e3b"
|
||
SHOW_TEXT_CLAUSE_LEN = 1179
|
||
SHOW_TEXT_CLAUSE_PREFIX = (
|
||
"`show_text` displays part of a document to the user EXACTLY as stored"
|
||
)
|
||
|
||
|
||
def test_show_text_clause_byte_locked() -> None:
|
||
"""The clause is LOCKED copy: sha256 + exact prefix + total length,
|
||
plus the two sentences the phase's contract depends on verbatim (the
|
||
copy-from-the-display rule and the server-owns-the-numbers rule)."""
|
||
assert len(SHOW_TEXT_CLAUSE) == SHOW_TEXT_CLAUSE_LEN
|
||
assert _sha256(SHOW_TEXT_CLAUSE) == SHOW_TEXT_CLAUSE_SHA256
|
||
assert SHOW_TEXT_CLAUSE.startswith(SHOW_TEXT_CLAUSE_PREFIX)
|
||
assert SHOW_TEXT_CLAUSE.endswith("For an image use `show_image` instead.")
|
||
assert "the user copies from what is displayed, not from your prose" in SHOW_TEXT_CLAUSE
|
||
assert "the numbers you display are the server's, never yours" in SHOW_TEXT_CLAUSE
|
||
|
||
|
||
def test_high_prompt_display_clause_composition_is_byte_locked() -> None:
|
||
"""One mechanism, four combinations (phase 182, task 04): neither
|
||
flag reproduces the untouched :data:`TOOLS_SECTION` tail,
|
||
``show_image`` alone reproduces the phase-124 bytes exactly,
|
||
``show_text`` alone appends only the text clause, and both append
|
||
the image clause, a blank line, the text clause, then the closing
|
||
tag — ``TOOLS_SECTION`` itself is never edited (its own sha pin
|
||
above holds)."""
|
||
off = build_high_prompt([])
|
||
assert off.endswith(TOOLS_SECTION)
|
||
head = off[: -len("</tools>")]
|
||
assert build_high_prompt([], show_image=True) == head + SHOW_IMAGE_CLAUSE + "\n</tools>"
|
||
assert build_high_prompt([], show_text=True) == head + SHOW_TEXT_CLAUSE + "\n</tools>"
|
||
assert (
|
||
build_high_prompt([], show_image=True, show_text=True)
|
||
== head + SHOW_IMAGE_CLAUSE + "\n\n" + SHOW_TEXT_CLAUSE + "\n</tools>"
|
||
)
|