phase: 158_thinking_timeline
Build and Push Containers / build-and-push-app (push) Successful in 8m38s
Build and Push Containers / build-and-push-db (push) Successful in 32s

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.
This commit is contained in:
2026-10-05 02:52:55 -04:00
parent 3f752fb77f
commit 31bd903a93
49 changed files with 4280 additions and 482 deletions
+20 -15
View File
@@ -174,6 +174,7 @@ def test_tool_line_clock_state_is_turn_scoped_module_state() -> None:
"let toolLineTimer = 0",
"let toolLineStart = 0",
"let toolLineWrap = null",
"let toolLineEl = null",
):
assert decl in js, f"{decl} must be module-scope state"
anchor = js.find("let turnTimeoutCb = null")
@@ -196,11 +197,11 @@ def test_arm_has_exactly_one_live_call_site() -> None:
tool_idx = js.find('ev.type === "tool"')
delta_idx = js.find('ev.type === "delta"')
branch = js[tool_idx:delta_idx]
assert "armToolLineClock(wrap)" in branch, "the live tool branch arms the clock"
append = branch.find(
"appendToolLine(wrap, name, argument, frameOffset, frameLimit, framePath)"
assert "armToolLineClock(wrap, line)" in branch, (
"the live tool branch arms the clock with the tracked line (phase 158)"
)
arm = branch.find("armToolLineClock(wrap)")
append = branch.find("const line = appendToolLine(")
arm = branch.find("armToolLineClock(wrap, line)")
assert -1 < append < arm, (
"the arm follows the line's append — the baseline resets per line"
)
@@ -281,29 +282,33 @@ def test_suffix_is_textcontent_only_on_the_latest_line() -> None:
Reading src/app.py (12s)" — the typing hint stays bare "Ns") written
with ``textContent`` on a ``createElement`` span — never innerHTML —
appended as a SIBLING after the line's existing children (the pinned
template text + the <code> argument). It targets the LATEST line only
(`.tool-call:last-child` — older lines keep their permanent record),
and a missing container (New-Chat click mid-gap) makes the tick a
no-op (the null-safe chain is the guard)."""
template text + the <code> argument). Phase 158 (D1): it targets
the TRACKED line only (the arm's second param — the
``:last-child`` query is gone; multiple disclosures make it the
wrong line), and a missing reference (New-Chat click mid-gap)
makes the tick a no-op (the null guard)."""
js = _js()
fn = js.find("function armToolLineClock")
assert fn != -1, "armToolLineClock must exist"
body = js[fn : js.find("\n}\n", fn)]
assert 'querySelector?.(".tool-calls .tool-call:last-child")' in body, (
"the suffix targets the LATEST line only"
assert ".tool-calls .tool-call:last-child" not in body, (
"the :last-child query is GONE (multi-disclosure — phase 158)"
)
assert "toolLineEl = lineEl" in body, "the tracked line reference"
assert "if (!toolLineEl) return" in body, (
"null-guarded: a wrap reset mid-gap (New Chat) is a no-op"
)
assert 'toolLineEl.querySelector(".tool-elapsed")' in body, (
"find-or-create the suffix span on the tracked line"
)
assert 'line.querySelector(".tool-elapsed")' in body, "find-or-create the suffix span"
assert 'el.className = "tool-elapsed"' in body
assert 'document.createElement("span")' in body, "the span is createElement'd"
assert "line.appendChild(el)" in body, (
assert "toolLineEl.appendChild(el)" in body, (
"the suffix is a SIBLING appended AFTER the line's existing children"
)
assert "`(${secs}s)`" in body, ("the parenthesized '(Ns)' suffix, textContent-built")
assert "innerHTML" not in body, "no HTML write in the clock — textContent only"
assert "TOOL_LINE_ELAPSED_AFTER_MS" in body, "the tick gates on the named constant"
assert "toolLineWrap?.querySelector?" in body, (
"null-safe: a wrap reset mid-gap (New Chat) makes the tick a no-op"
)
def test_tool_line_template_literals_stay_byte_identical() -> None: