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.
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user