phase: 98_sync_summary_visibility
All verification complete. Final report: **Phase 98 — Sync summary visibility: final verification pass** (all 5 tasks already complete; implementation verified against the design, no defects found, no code changes needed) - **Implementation checked:** `SyncStatus` phase machine (4 new keys, terminal-keep counts), `on_progress` hook in `generate_folder_summaries`, `summary_pending` on `KbTreeSource`/`KbTreeFolder` + D3 rule in `build_kb_tree`, phase-aware sync labels + pending UI in `sources.js`, `.kb-summary-pending` CSS — all match decisions D1–D5. - **Unit + integration:** `uv run pytest` → 2184 tests, 0 failed/errors (exit 0) - **Coverage:** `uv run pytest --cov=app --cov-report=term-missing` → **99%** on `app/` (criterion >90% ✓; `app/api/sync.py` and `app/rag/folder_summaries.py` at 100%) - **Lint/types:** `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors, 0 warnings - **Phase E2E (isolation):** `uv run pytest tests/e2e/test_sync_summary_visibility.py -v --no-cov` → **3 passed** (phase machine, live label, pending markers + gap-fill self-heal) - **Regression suites (each isolated, `--no-cov`):** test_kb_tree ✓, test_ls_tree_drilldown 3 ✓, test_sync_button 3 ✓, test_sync_upload_progress 4 ✓, test_oneshot_llm_retry 2 ✓, test_local_directory_sources 3 ✓ - **Completion criteria:** all 7 verified green — status phase fields + terminal semantics; `Writing KB overview…`/`Summarizing folders… (n/m)` labels (title + aria-live); pending set == `missing_folder_summaries` (integration cross-check pinned at `test_docs_api.py:428`); CLI/`ls` byte-identity (no changes to those paths, pins green); suite/coverage/lint gates; dedicated + regression E2E. Commit left to the harness per protocol (no `git add`/`commit` run). - **Decisions/deviations:** none — no fixes were required this pass. - **Next pending phase:** `99_kb_tree_table_and_back_nav`.
This commit is contained in:
@@ -729,6 +729,13 @@ def test_api_changed_sync_generates_folder_rows(
|
||||
assert body["detail"]["added"] == 2
|
||||
assert body["detail"]["overview"] is True
|
||||
assert body["detail"]["sources_version"] == 1
|
||||
# Phase 98 (task 01): the progress hook fired on the CHANGED-KB
|
||||
# regeneration branch — the terminal clears phase + current_summary
|
||||
# and keeps the hook's final counts (2 candidates: root + a/ — the
|
||||
# only writer of those counters is the hook itself).
|
||||
assert body["phase"] is None
|
||||
assert body["current_summary"] is None
|
||||
assert body["summaries_done"] == 2 and body["summaries_total"] == 2
|
||||
# One LLM client for probe + import + overview + folder summaries;
|
||||
# exactly two FOLDER_SUMMARY_MODE calls (root + the 2-doc a/ folder).
|
||||
assert len(clients) == 1
|
||||
@@ -770,6 +777,15 @@ def test_api_unchanged_resync_burns_zero_folder_calls(
|
||||
body = _poll(sync_client, "success")
|
||||
assert body["detail"]["overview"] is False
|
||||
assert body["detail"]["sources_version"] == 1 # unchanged → no bump
|
||||
# Phase 98 (task 01): the no-gap skip branch never CALLS the
|
||||
# generator — the hook never fires, so the terminal's summary
|
||||
# counters stay 0/0 (the run stayed in the import phase through to
|
||||
# the terminal, which then cleared it). The counter keep at 0/0 is
|
||||
# the deterministic pin of "never fired": a fired hook's counts
|
||||
# would survive to the terminal (the keep-final-counts rule).
|
||||
assert body["phase"] is None
|
||||
assert body["current_summary"] is None
|
||||
assert body["summaries_done"] == 0 and body["summaries_total"] == 0
|
||||
assert len(clients) == 2
|
||||
# Zero GENERATION calls — no folder summary, no KB overview. The
|
||||
# only chat the re-sync's client makes is the phase-41 probe's ping.
|
||||
@@ -849,6 +865,12 @@ def test_api_unchanged_resync_with_gap_fills_only_the_missing_row(
|
||||
"chunks", "summaries", "summary_errors", "overview",
|
||||
"sources_version",
|
||||
}
|
||||
# Phase 98 (task 01): the progress hook fired on the UNCHANGED-KB
|
||||
# GAP-FILL branch — total is the missing count (the one deleted
|
||||
# row), kept at the terminal next to the cleared phase keys.
|
||||
assert body["phase"] is None
|
||||
assert body["current_summary"] is None
|
||||
assert body["summaries_done"] == 1 and body["summaries_total"] == 1
|
||||
assert len(clients) == 2
|
||||
# Targeted fill — exactly ONE folder call, the missing folder only
|
||||
# (plus the phase-41 probe's ping on the same client).
|
||||
@@ -910,6 +932,11 @@ def test_api_folder_lite_failure_keeps_rows_stays_green_and_bumps(
|
||||
assert body["error"] is None
|
||||
assert body["detail"]["updated"] == 1
|
||||
assert body["detail"]["sources_version"] == 2 # the bump was not blocked
|
||||
# Phase 98 (task 01): the failed (fail-soft) folder still advanced
|
||||
# the hook's counter — the hook fires BEFORE the attempt, so the
|
||||
# terminal keeps 2/2, not 1/2.
|
||||
assert body["phase"] is None and body["current_summary"] is None
|
||||
assert body["summaries_done"] == 2 and body["summaries_total"] == 2
|
||||
assert current_sources_version(db) == 2
|
||||
rows_after = _rows(db)
|
||||
assert rows_after["LocalDocs", "a"] == rows_before["LocalDocs", "a"]
|
||||
@@ -951,6 +978,11 @@ def test_api_empty_table_first_sync_regenerates(
|
||||
|
||||
assert body["detail"]["overview"] is False # unchanged → no overview
|
||||
assert body["detail"]["sources_version"] == 1 # unchanged → no bump
|
||||
# Phase 98 (task 01): the gap-fill branch over the emptied table —
|
||||
# every candidate missing (2), the hook's final counts kept at the
|
||||
# terminal next to the cleared phase keys.
|
||||
assert body["phase"] is None and body["current_summary"] is None
|
||||
assert body["summaries_done"] == 2 and body["summaries_total"] == 2
|
||||
assert len(clients) == 2
|
||||
assert len(_folder_calls(clients[1])) == 2 # the folders regenerated
|
||||
assert set(_rows(db)) == {("LocalDocs", ""), ("LocalDocs", "a")}
|
||||
|
||||
Reference in New Issue
Block a user