Files
brain-of-reese/tests/unit/test_tool_call_compact.py
T
ducoterra 31bd903a93
Build and Push Containers / build-and-push-app (push) Successful in 8m38s
Build and Push Containers / build-and-push-db (push) Successful in 32s
phase: 158_thinking_timeline
All verification complete. Every gate is green and the docs check out. Final report:

## Phase 158 final verification pass — COMPLETE

**Verified (all tasks pre-completed; this pass re-ran the full gate):**
- Unit + integration: `uv run pytest --cov=app --cov-report=term-missing` → **3618 passed, 99% coverage on `app/`** (>90% gate)
- Phase E2E in isolation: `uv run pytest tests/e2e/test_thinking_timeline.py -v --no-cov` → **6/6 passed** (order pin, fold-at-rest, live-open, restore+`segments` array, shared page, no-thinking single disclosure)
- Regression E2E, each in isolation: `test_thinking_display` 5✓, `test_tool_call_compact` 4✓, `test_turn_progress_loader` 3✓ (per-run pin updated), `test_big_read_progress` 4✓, `test_agent_document_tools` 4✓, `test_share_chat` 4✓, `test_chat_persistence` 4✓, `test_smoke` 3✓
- Lint/types: `uv run ruff check . && uv run pyright` → **clean (0 errors)**

**Completion criteria:** all 9 met — timeline E2E pins ✓, restore/shared `segments` ✓, no-thinking single-disclosure + the three UNCHANGED regression suites green ✓, pre-phase byte-identical round-trip (integration pin: key absent, never null; >50 / >32 000 / empty text / unknown kind all 422) ✓, full suite + coverage + E2E + lint ✓, `git diff --stat` footprint limited to `app/schemas.py`, `frontend/assets/{app,shared}.js`, `tests/`, README, phase files (a pre-existing owner `TODO.md` edit is out of scope, not staged by the phase commit) ✓, D15→per-run supersession recorded in the updated pin ✓. (Commit + phase move are the harness's job; task dirs already in `complete/`.)

**Documentation:** README.md — Thinking section correctly describes the phase-158 timeline; config reference matches `.env.example` exactly; page table matches registered routes; no stale single-block wording. AGENTS.md — rules and quick-reference commands all accurate. **Verified consistent — no changes needed.**

**Decisions/deviations:** none. `test_tool_call_compact.py` carries a one-line docstring-only update (referenced the removed `closeToolCalls`); its test logic is byte-identical to the phase-117 contract and green.

**Next pending phase:** none — `todo/` contains only 158.
2026-10-05 02:52:55 -04:00

278 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unit: the phase-117 tool-calls DISCLOSURE contract (task 01).
The owner's visual-glitch report (2026-09-15): one per-call tool "card"
per ``tool`` frame — 6+ stacked full-width bordered cards on a phone —
swamped a completed answer, and the flex row broke the label
mid-word. The fix (phase 117, owner decisions D1–D6) wraps the
existing ``.tool-calls`` list in a native ``details`` disclosure with a
plain-text "Tool call(s) (N)" summary — phase 158 (D1): one
disclosure per CONTIGUOUS tool-call run, created OPEN on the first
live ``tool`` frame of the run, folded at rest (delta folds the
active run via ``closeCurrentRun``; done/stop/finalizeFailedTurn fold
every run via ``closeAllRuns``), and rendered CLOSED on the restore
path and the shared page (D3). The count is per run (the per-run
"Tool call(s) (N)", D3). The line bytes, the four label literals, and
the ``<code>`` textContent arguments stay byte-identical (D6), so the
phase-37/70/87/95 pins stay green.
No ``app/`` logic (frontend-only, D1) — like
``test_frontend_tool_states.py``, this module pins the JS/CSS markers
at source level: the disclosure/summary build (task 01) and the
debox/dechip + disclosure CSS contract (task 02). The E2E story suite
(task 03) completes the phase.
"""
from __future__ import annotations
import re
from pathlib import Path
FRONTEND = Path(__file__).resolve().parents[2] / "frontend"
APP_JS = FRONTEND / "assets" / "app.js"
SHARED_JS = FRONTEND / "assets" / "shared.js"
STYLES_CSS = FRONTEND / "assets" / "styles.css"
#: The four pinned tool-line label literals (D6) — the emoji guard and
#: the phase-37/70 pins key off these exact bytes.
PINNED_LABELS = (
'line.textContent = "📄 Reading "',
'line.textContent = "🔎 Searching for "',
'line.textContent = "🔎 Listing documents in "',
'line.textContent = "🔎 Listing documents"',
)
def _js() -> str:
return APP_JS.read_text(encoding="utf-8")
def _shared_js() -> str:
return SHARED_JS.read_text(encoding="utf-8")
def _css() -> str:
return STYLES_CSS.read_text(encoding="utf-8")
def _fn(src: str, name: str) -> str:
"""The flat function body (house extraction: first ``\n}\n`` after
the definition — the function must stay flat, no nested
declarations)."""
fn = src.find(f"function {name}")
assert fn != -1, f"{name} must exist"
return src[fn : src.find("\n}\n", fn)]
def test_tool_run_builds_an_open_details_disclosure() -> None:
"""Phase 158 (D1/D2/D5): ``ensureToolRun`` creates a native
``details.tool-calls-disclosure`` via ``createToolDisclosure`` —
a ``summary.tool-calls-summary`` (first child) + the existing
``.tool-calls`` list (role=list + aria-label "Tool calls",
second child) — inserted before the bubble in arrival order (D2).
It starts OPEN while the run is live; every append re-renders the
plain-text per-run "Tool call(s) (N)" count on the summary
through textContent (no innerHTML anywhere)."""
js = _js()
disc = _fn(js, "createToolDisclosure")
assert "document.createElement(\"details\")" in disc
assert "className = \"tool-calls-disclosure\"" in disc
assert "document.createElement(\"summary\")" in disc
assert "className = \"tool-calls-summary\"" in disc
# The list keeps its exact phase-37 shape, nested in the disclosure.
assert "className = \"tool-calls\"" in disc
assert 'setAttribute("role", "list")' in disc
assert 'setAttribute("aria-label", "Tool calls")' in disc
assert "innerHTML" not in disc, (
"the tool builder stays createElement/textContent (phase 117 "
"style — innerHTML ABSENT)"
)
run = _fn(js, "ensureToolRun")
assert "createToolDisclosure()" in run
assert "disc.open = true", (
"D5: created OPEN on the first live tool frame of the run — the "
"calls and the phase-87 .tool-elapsed suffix stay visible during "
"the turn"
)
assert 'insertBefore(disc, body.querySelector(".bubble"))' in run, (
"D2: above the answer, in arrival order"
)
# D5: the count rides the summary — the template fragment itself
# (singular for one call, plural otherwise), textContent-built.
append = _fn(js, "appendToolLine")
assert "Tool call${n === 1 ? \"\" : \"s\"} (${n})" in append, (
"the per-run plain-text count template (D3) on every append"
)
assert "innerHTML" not in append, (
"no HTML injection surface on tool lines — textContent only"
)
def test_append_tool_line_keeps_the_four_pinned_labels() -> None:
"""D6: the four ``line.textContent`` label literals and the
``<code>`` textContent arguments stay byte-identical — the emoji
guard, the phase-37/70 unit pins, and the E2E text assertions all
key off these exact bytes."""
body = _fn(_js(), "appendToolLine")
for label in PINNED_LABELS:
assert label in body, f"pinned label literal changed: {label!r}"
assert "code.textContent = argument" in body, (
"the path/pattern/scope is data — textContent, never innerHTML"
)
def test_close_all_runs_defined_and_called_at_every_rest_site() -> None:
"""Phase 158 (D5): ``closeAllRuns`` — the terminal settle (the
phase-117 ``closeToolCalls`` + the D15 block close, merged):
closes EVERY open ``details.thinking`` +
``details.tool-calls-disclosure`` in the wrap (defensive; a late
double-close is a no-op), called at ALL rest sites — the done
handler (the turn ended), the stop/abort settle (the turn was
stopped), and the failed-turn funnel — so the definition + ≥3
calls. The `delta` handler folds only the active run
(``closeCurrentRun``)."""
js = _js()
body = _fn(js, "closeAllRuns")
assert "details.thinking, details.tool-calls-disclosure" in body, (
"targets both run kinds in the given wrap"
)
assert "el.open = false" in body, "fold: open=false (idempotent)"
assert js.count("closeAllRuns(wrap)") >= 3, (
"the definition + the done handler + the stop/abort settle + "
"the failed-turn funnel — the runs settle exactly where the "
"Thinking blocks settle"
)
delta_idx = js.find('ev.type === "delta"')
done_idx = js.find('ev.type === "done"')
delta_branch = js[delta_idx:done_idx]
assert "closeCurrentRun()" in delta_branch, (
"the delta folds the ACTIVE run (not every run — a later "
"thinking frame opens a new one)"
)
def test_restore_path_folds_the_disclosure() -> None:
"""renderStoredMessage (D3): a restored brain record with `tools`
re-renders through the SAME appendToolLine (byte-identical — the
caller passes the run's disclosure, phase 158), and the
disclosure is FOLDED right after the loop (``restoreDisc.open =
false``) — a completed turn from storage is one compact
"Tool calls (N)" line, not N stacked cards (the space fix;
mirrors the thinking restore rendering collapsed)."""
js = _js()
fn = js.find("function renderStoredMessage")
assert fn != -1
body = js[fn : js.find("function restoreConversation", fn)]
assert "appendToolLine(restoreDisc, t.name, arg, tOffset, tLimit, tPath)" in body
assert "restoreDisc.open = false" in body
assert (
body.find("appendToolLine(restoreDisc, t.name, arg, tOffset, tLimit, tPath)")
< body.find("restoreDisc.open = false")
), "the fold sits AFTER the tool-line restore loop"
def test_run_anchors_are_the_bubble_in_arrival_order() -> None:
"""Phase 158 (D2): the anchor is the BUBBLE for both run kinds —
the old ``.tool-calls-disclosure ?? .tool-calls ?? .bubble``
anchor chain is GONE (with per-run disclosures, "above the
tool lines" was the wrong order — arrival order is the
contract), and the runs still open while active (D5)."""
js = _js()
run = _fn(js, "ensureThinkingRun")
assert 'querySelector(".bubble")' in run
assert "block.open = true" in run
assert "??" not in run, "the old anchor chain is GONE"
d = _fn(js, "ensureToolRun")
assert 'querySelector(".bubble")' in d
assert "disc.open = true" in d
def test_shared_page_add_tool_lines_builds_a_closed_disclosure() -> None:
"""shared.js addToolLines (D3 parity): the SAME
``details.tool-calls-disclosure`` + summary + ``.tool-calls`` list —
but created CLOSED (a pure render has no live turn to open it),
with the count set once at the end. The four label literals, the
three textContent-only argument lines, and the phase-95 marker stay
byte-identical (no innerHTML anywhere)."""
body = _fn(_shared_js(), "addToolLines")
assert "document.createElement(\"details\")" in body
assert "className = \"tool-calls-disclosure\"" in body
assert "document.createElement(\"summary\")" in body
assert "open = false" in body, (
"D3: the shared page renders the disclosure CLOSED — never an "
"auto-expanded record"
)
assert "className = \"tool-calls\"" in body
for label in PINNED_LABELS:
assert label in body, f"pinned label literal changed: {label!r}"
assert "Tool call${n === 1 ? \"\" : \"s\"} (${n})" in body, (
"the same plain-text count template (D5), set once at the end"
)
assert body.count("code.textContent = argument") == 3, (
"all three argument-bearing lines (read / grep / ls) stay "
"textContent-only"
)
assert "innerHTML" not in body, (
"no HTML injection surface on shared tool lines — textContent only"
)
def test_tool_call_rule_is_deboxed_inline_flow() -> None:
"""D4: the ``.tool-call`` rule is DEBOXED — no ``display: flex``,
no card (no background / border / radius / padding), and no
``var(--accent-line)`` left border. The accent now rides the TEXT
(``var(--accent-ink)``, ≈10.4:1 on the surface), and the line keeps
the compact status metrics + ``overflow-wrap: anywhere`` so the
label + inline ``code`` flow as ONE continuous run — a long path
wraps to the left edge and the label never breaks mid-word (the
"Rea/ding" glitch is gone)."""
m = re.search(r"\.tool-call \{([^}]*)\}", _css())
assert m, "the .tool-call rule must exist"
row = m.group(1)
assert "display: flex" not in row, (
"deboxed: no flex — the label + code are one inline run"
)
assert "var(--accent-line)" not in row, "deboxed: no accent left border"
assert "border" not in row, "deboxed: no card borders at all"
assert "background" not in row, "deboxed: no card background"
assert "padding" not in row, "deboxed: no card padding"
assert "var(--accent-ink)" in row, (
"the accent now rides the text color (≈10.4:1 on the surface)"
)
assert "font-size: 0.8rem" in row, "the compact status metrics stay"
assert "overflow-wrap: anywhere" in row, "the path wraps inside the run"
def test_tool_call_code_rule_is_dechipped() -> None:
"""D4: the ``.tool-call code`` rule KEEPS ``var(--mono)`` +
``var(--ink)`` (≈11.5:1 on the surface — AA) but LOSES the chip
(no background, no padding, no radius) — the path is inline text
in the line's run now."""
m = re.search(r"\.tool-call code \{([^}]*)\}", _css())
assert m, "the .tool-call code rule must exist"
code = m.group(1)
assert "var(--mono)" in code
assert "var(--ink)" in code
assert "background" not in code, "dechipped: no chip background"
assert "padding" not in code, "dechipped: no chip padding"
assert "border-radius" not in code, "dechipped: no chip radius"
assert "overflow-wrap: anywhere" in code
def test_tool_calls_disclosure_and_summary_rules_exist() -> None:
"""Phase 117 (D2/D4): the NEW ``.tool-calls-disclosure`` and
``.tool-calls-summary`` rules exist — the summary is a native
focusable toggle carrying the accent TEXT (``var(--accent-ink)``,
≈10.4:1 on the surface; text + color, never color alone, B5),
modeled on the details.thinking family, with no new color literal
(the phase-92 zero-literal invariant)."""
css = _css()
assert re.search(r"\.tool-calls-disclosure \{", css), (
"the .tool-calls-disclosure rule must exist"
)
m = re.search(r"\.tool-calls-summary \{([^}]*)\}", css)
assert m, "the .tool-calls-summary rule must exist"
summary = m.group(1)
assert "var(--accent-ink)" in summary, (
"the accent rides the summary text (≈10.4:1 on the surface)"
)
assert "cursor: pointer" in summary, "a toggle affordance"