phase: 182_verbatim_show_text
**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:
@@ -64,6 +64,25 @@ const invalidEl = document.querySelector("#shared-invalid");
|
||||
* literal is only the no-config fallback. */
|
||||
const brand = () => window.BOR_BRAND || "Brain of Reese";
|
||||
|
||||
/* Phase 182 (task 07): the shared page's verbatim link target — the plain
|
||||
* `/document.html?source=…&path=…` href, with NO modal hook (this page has
|
||||
* no document modal, and the documents API is admin-gated: a guest who
|
||||
* follows the link lands on the sign-in page, which is the honest answer to
|
||||
* "show me that file"). verbatim.js builds this same href by default; naming
|
||||
* it here is the parity statement — the SAME panel client, the SAME three
|
||||
* globals the chat page calls, so a shared turn renders identically to the
|
||||
* owner's (the phase's hard requirement). */
|
||||
const SHARED_VERBATIM_OPTS = {
|
||||
documentHref: (
|
||||
source,
|
||||
path
|
||||
) =>
|
||||
"/document.html?source=" +
|
||||
encodeURIComponent(source) +
|
||||
"&path=" +
|
||||
encodeURIComponent(path),
|
||||
};
|
||||
|
||||
/* ---------- the token (from the URL) ----------
|
||||
* The last path segment of /shared/<token>. A malformed or missing
|
||||
* token (a path without a final segment, or a non-uuid segment) →
|
||||
@@ -608,9 +627,13 @@ function addAnswerImage(wrap, ref) {
|
||||
* the deflection treatment + the plain-text "Maybe try" chips, the
|
||||
* brain answer's image figure (phase 124, task 06 — a plain
|
||||
* non-interactive <figure>), the plain-text source chips, and the
|
||||
* stopped note. NO interactive markup is ever created here — no
|
||||
* stopped note. NO interactive markup is ever created HERE — no
|
||||
* buttons, no forms, no links, no click handlers (owner-locked:
|
||||
* zero controls). Markdown goes
|
||||
* zero controls). The ONE exception is phase 182's verbatim panel, and it
|
||||
* is not built HERE either: hydrateVerbatim / appendUnlocatedVerbatim /
|
||||
* enhanceAnswerCodeBlocks (assets/verbatim.js, the classic-script client the
|
||||
* chat page calls too) own that Copy button and that plain viewer link —
|
||||
* parity first, so a guest reads the owner's answer. Markdown goes
|
||||
* through the global escape-first renderMarkdown (markdown.js): the
|
||||
* stored payloads are raw text, so the renderer's XSS safety applies
|
||||
* unchanged. */
|
||||
@@ -628,7 +651,12 @@ function renderSharedMessage(m) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
const wrap = addSharedMessage("brain", renderMarkdown(m.text));
|
||||
// Phase 182 (task 07): the shared page's answer renderer OPTS IN the
|
||||
// verbatim pass — the same opt-in as the chat page's restore, so a stored
|
||||
// ` ```bor ` fence paints the same placeholder and hydrates from the
|
||||
// record's `answer_text` below. The USER bubble above stays one-argument
|
||||
// (a question is never a locator).
|
||||
const wrap = addSharedMessage("brain", renderMarkdown(m.text, { verbatim: true }));
|
||||
// Phase 158 (task 03, D4): a record WITH `segments` renders the
|
||||
// per-run timeline through addSegments (collapsed — the parity
|
||||
// copy of the chat page's restoreTimeline); the runs carry the
|
||||
@@ -654,8 +682,24 @@ function renderSharedMessage(m) {
|
||||
// (below the text, above the chip row). A record without the key
|
||||
// renders byte-identically (no figure DOM).
|
||||
addAnswerImage(wrap, m.answer_image);
|
||||
// Phase 182 (task 07): the verbatim panels render with the bubble in the
|
||||
// SAME order as the chat page's restore — hydrate the placeholders inside
|
||||
// the bubble, append the blocks the answer never located, then the source
|
||||
// chips (below the text, above the clusters). The panel DOM, the Copy
|
||||
// button and the unresolved sentence all come from verbatim.js — the ONE
|
||||
// implementation shared with the chat page (parity, not a second copy);
|
||||
// this file still builds no control of its own.
|
||||
hydrateVerbatim(
|
||||
wrap.querySelector(".bubble"),
|
||||
m.answer_text || [],
|
||||
SHARED_VERBATIM_OPTS
|
||||
);
|
||||
appendUnlocatedVerbatim(wrap, m.answer_text || [], SHARED_VERBATIM_OPTS);
|
||||
addSources(wrap, m.sources);
|
||||
if (m.stopped) addStoppedNote(wrap);
|
||||
// The settled answer's ordinary code blocks get the same Copy button the
|
||||
// chat page gives them (no badge, no caption — locked A5).
|
||||
enhanceAnswerCodeBlocks(wrap);
|
||||
}
|
||||
|
||||
/* ---------- the public read ----------
|
||||
|
||||
Reference in New Issue
Block a user