phase: 95_read_truncation_cap
All gates are green. Final verification is complete — no fixes were needed; the phase is fully implemented and passing. **Phase 95 final verification report** - Verified all 3 tasks already implemented (task files in `complete/`): read cap + notice + `ToolResultPiece` (task 01), `tool_result` SSE + live/saved/shared marker (task 02), E2E suite (task 03). - Code checks passed: `BOR_READ_MAX_CHARS` (default 128 000, validator), pinned `READ_TRUNCATION_NOTICE` + `TRUNCATION_MARKER`, byte-identical at/under-cap read, holder `read_truncations` (no `tool_calls` impact), piece order (after `tool`, before next round), `ChatToolResultEvent`, `ToolCall` back-compat fields, `app.js` live/restore handlers, `shared.js`, `.truncated-note` CSS, `.env.example` entry, mock-LLM echo script. - **Tests:** `uv run pytest` → 1966 passed; `uv run pytest --cov=app --cov-report=term-missing` → all green, TOTAL **99%** (>90% gate). - **E2E:** `uv run pytest tests/e2e/test_read_truncation_cap.py -v --no-cov` → **3 passed** (frame order + live marker + LLM notice via echo; save→shared fidelity; short-read control). - **Regression (isolated):** `test_agent_document_tools` 4 ✓, `test_chat_history` 5 ✓, `test_share_chat` 4 ✓, `test_big_read_progress` 4 ✓, `test_stop_generation` 3 ✓. - **Lint/types:** `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors, 0 warnings. **Completion criteria:** ① over-cap read → first-cap-chars + marker + pinned notice — ✓ (unit-pinned: at-cap/cap+1/notice tests); ② user marker live/saved/shared — ✓ (E2E + frontend tests); ③ at/under cap byte-identical, no frame — ✓ (unit + control E2E); ④ top-2 `<documents>` retrieval untouched — ✓ (`app/rag/retriever.py` unmodified vs HEAD); ⑤ suite green, >90% coverage, ruff+pyright clean — ✓; ⑥ no completed-phase behavior change — ✓ (all gates green; commit left to harness per pass rules). - No defects found; no changes made this pass. Next pending phase: none in `todo/` (96 is the next free number).
This commit is contained in:
@@ -153,7 +153,17 @@ function addThinkingBlock(wrap, thinking) {
|
||||
* line (no migration). The content marks are the exact app.js
|
||||
* template strings — the frontend emoji guard (tests/integration/
|
||||
* test_api.py) strips precisely those literals in this file, as in
|
||||
* app.js. */
|
||||
* app.js.
|
||||
*
|
||||
* Phase 95 (task 02): the truncation marker rides the SAME stored
|
||||
* record the chat page uses — a {name, argument, truncated,
|
||||
* chars_shown, chars_total} entry (the live `tool_result` frame's stamp,
|
||||
* persisted with the turn) re-renders the identical " (truncated —
|
||||
* showing N of M chars)" span next to its Reading line, so a shared
|
||||
* page shows the truncation pixel-identically to the chat page (the
|
||||
* phase-50 restore contract). A record saved before phase 95 (no
|
||||
* fields) renders exactly as before (no marker, no migration).
|
||||
* createElement + textContent only — nothing HTML-shaped from storage. */
|
||||
function addToolLines(wrap, tools) {
|
||||
if (!Array.isArray(tools) || !tools.length) return;
|
||||
const body = wrap.querySelector(".msg-body");
|
||||
@@ -192,6 +202,16 @@ function addToolLines(wrap, tools) {
|
||||
line.textContent = "🔎 Listing documents";
|
||||
}
|
||||
container.appendChild(line);
|
||||
// Phase 95: the stored truncation record — the same marker the chat
|
||||
// page's restore path renders (plain integers, no separators);
|
||||
// only argument-bearing (Reading) lines can carry it.
|
||||
if (t.truncated && argument) {
|
||||
const note = document.createElement("span");
|
||||
note.className = "truncated-note";
|
||||
note.textContent =
|
||||
" (truncated — showing " + (Number(t.chars_shown) || 0) + " of " + (Number(t.chars_total) || 0) + " chars)";
|
||||
line.appendChild(note);
|
||||
}
|
||||
}
|
||||
body.insertBefore(container, body.querySelector(".bubble"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user