phase: 99_kb_tree_table_and_back_nav
Build and Push Containers / build-and-push-app (push) Successful in 1m55s
Build and Push Containers / build-and-push-db (push) Successful in 12s

All verification is complete and green. Final report:

**Phase 99 — final verification pass (all 3 tasks already in `complete/`)**

- Verified the one-line Description clamp: `.kb-desc-cell` flex wrapper + `.kb-desc-text` ellipsis triad + `nowrap` column + in-cell Edit button + hover `title` + untouched level block (source diffs match D1; unit pins present)
- Verified the back-button breadcrumb nav: `applyTarget(target, push)` state-only `pushState`, no-duplicate gate, popstate adopt/reset, `bor:view-refresh` alignment before `loadTree()`, anonymous gate listener-free, router.js untouched (D2)
- `uv run pytest --cov=app`: **2180 passed**, coverage **99%** (>90% gate)
- `uv run ruff check .`: clean; `uv run pyright`: **0 errors, 0 warnings**
- `uv run pytest tests/e2e/test_kb_tree_nav.py -v --no-cov` (isolated, DB up): **4/4 passed** (back-walk, jump-then-back, fresh-nav/active-reclick, measured clamp incl. row-height parity ±4px, computed styles, full text in DOM/title/level block)
- Regression suites isolated: `test_kb_tree.py` 8✓, `test_ls_tree_drilldown.py` 3✓, `test_navbar_refresh.py` 7✓, plus `test_sync_summary_visibility.py` 3✓ (marker pin updated to `kb-desc-text kb-summary-pending` — the sanctioned class-pair change) and `test_edit_summaries.py` 4✓
- Criteria 1–4: **met** (each as above). Criterion 5 (atomic commit): left to the harness per executor rules — no `git add`/`commit` run; all changes left in the working tree
- No defects found in prior phases; no deviations. Stray `__pycache__/test_zz_smoke_tree_ui.*.pyc` is a leftover (no `.py` on disk) — inert, not touched
- Next pending phase: **none** — `todo/` will be empty once the harness moves this phase
This commit is contained in:
2026-09-13 01:57:40 -04:00
parent f665a83b1a
commit 8476dc1e07
22 changed files with 1755 additions and 53 deletions
+18 -7
View File
@@ -548,23 +548,34 @@ def _pin_refresh_listener(js: str, gate: str, listener_call: str, name: str) ->
"""Shared shape of the task-02 pin: the view module listens for
``bor:view-refresh`` on its own root, the listener re-runs the
view's existing load, and the listener is armed ONLY in the ADMIN
branch — after the whoami gate (anonymous never fetches)."""
branch — after the whoami gate (anonymous never fetches). The
body is sliced to the listener's close: the block-arrow forms
(phase 99 task 02: the rag view's history-alignment step runs
before the load) close with ``});``, the inline-arrow forms close
with ``());`` (the slice then runs to the NEXT ``});`` in the file
— or to its end — harmless, the inline call sits at the slice's
start)."""
listener = js.find('addEventListener("bor:view-refresh"')
assert listener != -1, f"{name} must listen for the refresh event on the view root"
gate_i = js.find(gate)
assert 0 <= gate_i < listener, (
f"{name}: the listener must be armed in the ADMIN branch (after {gate!r})"
)
assert listener_call in js[listener : listener + 120], (
end = js.find("});", listener)
body = js[listener:] if end == -1 else js[listener : end + 3]
assert listener_call in body, (
f"{name}: the listener must re-run the view's load ({listener_call!r})"
)
def test_rag_view_refetches_on_reshow() -> None:
"""Phase 77 task 02 (+ phase 97 task 04): the RAG (knowledge base)
view re-fetches on a user-initiated re-show — sources.js listens
and re-runs ``loadTree()`` (the phase-97 catalog load: ONE fetch of
``GET /api/docs/tree``). The load is race-tokened (phase 79) and
"""Phase 77 task 02 (+ phase 97 task 04, + phase 99 task 02):
the RAG (knowledge base) view re-fetches on a user-initiated
re-show — sources.js listens and re-runs ``loadTree()`` (the
phase-97 catalog load: ONE fetch of ``GET /api/docs/tree``), now
AFTER the phase-99 history alignment (adopt the entry's kb / reset
to the top — a block-arrow listener, hence the ``});`` close in
the pin helper below). The load is race-tokened (phase 79) and
the RE-ENTRANT render — ``renderLevel`` clears BOTH row containers
at the TOP before filling them (the History pattern from task 01,
extended to the folders table) — so a refresh from a populated
@@ -572,7 +583,7 @@ def test_rag_view_refetches_on_reshow() -> None:
of leaving ghost rows."""
js = _asset("sources.js")
_pin_refresh_listener(
js, "const admin = await fetchIsAdmin();", "() => loadTree()", "sources.js"
js, "const admin = await fetchIsAdmin();", "loadTree()", "sources.js"
)
load = js.find("async function loadTree()")
assert load != -1, "loadTree must exist"