phase: 182_verbatim_show_text
Build and Push Containers / build-and-push-app (push) Successful in 1m46s
Build and Push Containers / build-and-push-db (push) Successful in 12s

**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`).
This commit is contained in:
2026-10-15 08:33:12 -04:00
parent 814a400e6d
commit b34b8ca672
104 changed files with 10852 additions and 203 deletions
+70 -1
View File
@@ -13,7 +13,10 @@ the ``scripts/``-consumer surface (``import_extension_set``), and the
phase-172 thinking level (``llm_chat_thinking_level`` — the value the
LLM layer reads per request via ``get_settings()``; the off switch is
live-settable, A2) and its phase-178 summary twin (task 02:
``llm_summary_thinking_level``, default ``""`` = off, A2).
``llm_summary_thinking_level``, default ``""`` = off, A2), and the
phase-182 verbatim-display family (task 01: ``answer_text`` — the master
switch — plus the two ``show_text_max_*`` budgets, whose inert ``0``
values and reset-to-default resolve live with no restart).
The rows are inserted directly through the ``db`` session (the admin
API is task 04); the save itself is ``invalidate_settings()`` —
@@ -249,6 +252,72 @@ def test_llm_summary_thinking_level_live_apply_no_restart(
assert get_settings().llm_summary_thinking_level == ""
# ---------------------------------------------------------------------------
# the verbatim-display switch + budgets: save/reset resolve the live values
# (phase 182, task 01)
# ---------------------------------------------------------------------------
def test_verbatim_display_settings_live_apply_no_restart(
db: Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Phase 182 (task 01, LOCKED A8): the verbatim-display master switch
and its two budgets follow the overlay live — a save (the PUT's write,
minus the API: ``_save`` + ``invalidate_settings()`` is exactly what
the PUT/DELETE call after the write) resolves
``get_settings().answer_text`` / ``.show_text_max_chars`` /
``.show_text_max_blocks`` WITHOUT a restart (the chat request path
reads them per turn via ``get_settings()``), the ``0`` values resolve
live as INERT (not an error, not coerced to the default), the bool
accepts the env-native ``false``/``0`` spellings, and a reset (the
DELETE) returns the env default.
"""
# A deterministic base (machine-independent, the kill-switch test's
# idiom): the code defaults, no row in the table.
for var in (
"BOR_ANSWER_TEXT",
"BOR_SHOW_TEXT_MAX_CHARS",
"BOR_SHOW_TEXT_MAX_BLOCKS",
):
monkeypatch.delenv(var, raising=False)
invalidate_settings()
assert get_settings().answer_text is True
assert get_settings().show_text_max_chars == 12_000
assert get_settings().show_text_max_blocks == 5
# Save: every value resolves through the overlay, no restart.
_save(db, "answer_text", "false")
_save(db, "show_text_max_chars", "2500")
_save(db, "show_text_max_blocks", "1")
invalidate_settings()
assert get_settings().answer_text is False
assert get_settings().show_text_max_chars == 2500
assert get_settings().show_text_max_blocks == 1
# ``0`` is the inert state (the field is not treated as "unset"), and
# the bool's other env-native spelling resolves to True.
_save(db, "answer_text", "1")
_save(db, "show_text_max_chars", "0")
_save(db, "show_text_max_blocks", "0")
invalidate_settings()
assert get_settings().answer_text is True
assert get_settings().show_text_max_chars == 0
assert get_settings().show_text_max_blocks == 0
# The reset returns the env defaults (no process restart in between).
db.execute(
text(
"DELETE FROM app_settings WHERE key IN "
"('answer_text', 'show_text_max_chars', 'show_text_max_blocks')"
)
)
db.commit()
invalidate_settings()
assert get_settings().answer_text is True
assert get_settings().show_text_max_chars == 12_000
assert get_settings().show_text_max_blocks == 5
# ---------------------------------------------------------------------------
# the chat concurrency semaphore follows the saved cap
# ---------------------------------------------------------------------------