phase: 193_sync_remaining_count
**Phase 193 — final verification pass: all gates green.**
- **Verified (all 3 tasks already complete in `complete/193_sync_remaining_count/`):** `fmtSyncLabel`'s count part is `total > 0 && total - done > 0 ? \`${total - done} remaining\` : ""` (plain int, hidden at `N==0`, `total===0` → no part); docstrings updated; chunk/image/summaries parts + `counts_line` terminal + `app/` byte-untouched (`git diff --stat`: no `app/` files).
- **Completion criteria:** `N remaining` shape + marker pins ✅ · upload shares the builder (E2E upload arm) ✅ · chunk/image/summaries pins green ✅ · terminal `counts_line` green unedited + A4 title/`#sync-progress` pin ✅ · suite green, coverage 99% ✅ · lint/types clean ✅ · commit + `todo/→complete/` left to the harness (no git index/history commands run).
- `uv run pytest -q --cov=app --cov-report=term-missing` → **exit 0**, junit: **4792 tests, 0 failures/errors/skipped**, `TOTAL 7181 16 → 99%` (>90%)
- `uv run pytest tests/e2e/test_sync_remaining_count.py -v --no-cov` → **3 passed** (isolated, 53 s)
- Isolated `--no-cov` sweep of the 14 sync-lineage suites (label_mobile, upload_progress, chunk_progress, image_progress, progress_box, summary_visibility, archive_upload_sources, status_freshness, upload_no_scan, peak_memory_visible, session_reset, fast_path_deferred_content, git_freeze, smoke) → **14/14 exit 0**
- `uv run ruff check .` → All checks passed · `uv run pyright` → **0 errors**
- **Docs:** README.md reviewed in full (all 2408 lines) + AGENTS.md. Fixed 2 inconsistencies: the sync running-label clause claimed the upload label shows the count — now states it shares the ONE builder/shape but reports no counts since phase 90 (renders bare); phase-190's "the other ~72 rows" → "the other 83 rows of the panel's 86-row catalogue" (mechanically: 86 CATALOG rows, 84 overridable). `N remaining` already in the sync paragraph; task-01's `(n/m)` examples gone. AGENTS.md verified consistent (no clause names the running count part → unchanged per the phase's "do not add if absent"). Mechanically re-verified: all 86 `BOR_*` settings documented + defaults match, `.env.example` 88 vars covered, 0 broken README anchors, all cited file paths exist, 17 palette colors, migrations head `0030`, pages/shell routes, ports 8123/8901, E2E port allocations (new suite's 8211/8914 unique).
- **Notable:** task 02's documented deviation stands — `files_done` is the importer's 1-based *current* file, so the phase doc's `[1-4]` range is unreachable; count-bearing frames are `1 … N−1` (pinned). Pre-existing, untouched: `E2E_APP_PORT_TOOLBUDGET`/`SAVEDOC` both 8129 and `SYNCUP`/`SYCNCEL` both 8133 — harmless under the one-suite-at-a-time rule.
- **Next pending phase:** none — `193_sync_remaining_count` is the last entry in `.agents/phases/todo/`.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Phase 129 task 05 E2E (Playwright, mock-only): while a file imports,
|
||||
the sync status (endpoint + button label) shows the file's
|
||||
embedding-chunk position — ``Syncing… chunkprog/big.md (1/2) · chunk
|
||||
14/38`` — with ``chunks_total`` the file's TRUE content-chunk count and
|
||||
embedding-chunk position — ``Syncing… chunkprog/big.md 1 remaining ·
|
||||
chunk 14/38`` — with ``chunks_total`` the file's TRUE content-chunk count and
|
||||
``current_chunk`` the 1-based first chunk of the in-flight
|
||||
token-budgeted batch (batch granularity, assumption A6), climbing 1 →
|
||||
N through the file's embed batches, restarting per file, and cleared
|
||||
@@ -10,7 +10,10 @@ N through the file's embed batches, restarting per file, and cleared
|
||||
The dedicated story suite for ``129_sync_chunk_progress`` (A16 — one
|
||||
Playwright file per phase, run in isolation): the owner's report was
|
||||
that ``Syncing… name/of/file/here (2/3)`` "needs to show what chunk
|
||||
we're on out of how many".
|
||||
we're on out of how many" (the file count read ``(position/total)``
|
||||
then — phase 193 moved that part to the distance to the end,
|
||||
``N remaining``, on the owner's directive; the ``· chunk`` part this
|
||||
suite is about is untouched).
|
||||
|
||||
Run in isolation (DB must be up: ``podman compose up -d db``):
|
||||
|
||||
@@ -60,9 +63,12 @@ the same host) registered as a ``kind=local`` source (the
|
||||
pattern; no git anywhere): ``chunkprog/`` with EXACTLY TWO files, both
|
||||
root-level (no subfolders → the summaries candidate set is just the
|
||||
source root): ``big.md`` (the ~60 KB file above) + ``small.md`` (one
|
||||
tiny chunk). The sorted walk processes ``big.md`` FIRST, so the file
|
||||
counter reads ``(1/2)`` during the big span and ``(2/2)`` during the
|
||||
small one — and the small file's single chunk is the proof that the
|
||||
tiny chunk). The sorted walk processes ``big.md`` FIRST, so the label's
|
||||
phase-193 count part reads ``1 remaining`` during the big span and is
|
||||
HIDDEN during the small one (the final file: N = total - done = 0 — the
|
||||
endpoint's ``files_done``/``files_total`` stay the 1-based 1/2 then 2/2
|
||||
position; only the LABEL counts down) — and the small file's single
|
||||
chunk is the proof that the
|
||||
chunk position RESTARTS per file (the big file ends at its last
|
||||
batch's first index, the small file reports ``(1, 1)``).
|
||||
|
||||
@@ -98,7 +104,8 @@ Test → observable mapping (Playwright Mapping Rule):
|
||||
own 2 s poll): after clicking **Sync sources**, the page's own
|
||||
poll renders the import span's ``#sync-label`` parts —
|
||||
``.sync-label-main`` == ``Syncing… chunkprog/big.md``,
|
||||
``.sync-label-count`` == ``(1/2)``, and ``.sync-label-chunk``
|
||||
``.sync-label-count`` == ``1 remaining`` (phase 193), and
|
||||
``.sync-label-chunk``
|
||||
VISIBLE matching ``· chunk \\d+/\\d+`` with the total == the
|
||||
expected chunk count — and the button ``title`` (and the
|
||||
``#sync-progress`` box — the aria-live announcer, phase 150: the
|
||||
@@ -177,8 +184,9 @@ SLOW_DELAY_S = "1.5"
|
||||
#: the directory's basename is the source name, phase 38).
|
||||
SOURCE = "chunkprog"
|
||||
|
||||
#: The two fixture files (sorted walk: ``big.md`` FIRST → the file
|
||||
#: counter reads (1/2) on the big span, (2/2) on the small one).
|
||||
#: The two fixture files (sorted walk: ``big.md`` FIRST → the label's
|
||||
#: phase-193 count part reads `1 remaining` on the big span and is
|
||||
#: HIDDEN on the small one — the final file, N = total - done = 0).
|
||||
BIG_FILE = f"{SOURCE}/big.md"
|
||||
SMALL_FILE = f"{SOURCE}/small.md"
|
||||
N_FILES = 2
|
||||
@@ -314,12 +322,12 @@ def _dedup_consecutive(values: list[int]) -> list[int]:
|
||||
#: Test 2, step 1 — the in-flight IMPORT label with the VISIBLE chunk
|
||||
#: part: the main part carries "Syncing… chunkprog/big.md" (the
|
||||
#: untruncated A4 text — the big file is the first of the two, so the
|
||||
#: count part is "(1/2)"), the pinned chunk part is visible matching
|
||||
#: phase-193 count part is "1 remaining"), the pinned chunk part is visible matching
|
||||
#: "· chunk n/m" with m == the expected chunk count (the arg), and the
|
||||
#: button title + the #sync-progress box (the aria-live announcer,
|
||||
#: phase 150) carry the UNTRUNCATED concatenation
|
||||
#: (main + " " + count + " " + chunk — A4: the announcer hears the
|
||||
#: whole "Syncing… <file> (1/2) · chunk n/m").
|
||||
#: whole "Syncing… <file> 1 remaining · chunk n/m").
|
||||
_JS_IMPORT_CHUNK_SNAPSHOT = """
|
||||
(n) => {
|
||||
const label = document.querySelector('#sync-label');
|
||||
@@ -330,7 +338,7 @@ _JS_IMPORT_CHUNK_SNAPSHOT = """
|
||||
const chunk = label.querySelector('.sync-label-chunk');
|
||||
if (!main || !count || !chunk || count.hidden || chunk.hidden) return null;
|
||||
if (main.textContent !== 'Syncing… chunkprog/big.md') return null;
|
||||
if (count.textContent !== '(1/2)') return null;
|
||||
if (count.textContent !== '1 remaining') return null;
|
||||
const m = chunk.textContent.match(/^· chunk (\\d+)\\/(\\d+)$/);
|
||||
if (!m || Number(m[2]) !== n) return null;
|
||||
const full = main.textContent + ' ' + count.textContent + ' ' + chunk.textContent;
|
||||
@@ -861,8 +869,8 @@ def test_endpoint_reports_the_chunk_position(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. UI — the label's chunk part: "Syncing… chunkprog/big.md (1/2) ·
|
||||
# chunk n/m" while a batch is in flight, the untruncated label on
|
||||
# 2. UI — the label's chunk part: "Syncing… chunkprog/big.md
|
||||
# 1 remaining · chunk n/m" while a batch is in flight, the untruncated label on
|
||||
# the title + announcer, the part cleared after the file's span
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -873,7 +881,8 @@ def test_label_shows_the_chunk_part(
|
||||
"""A fresh admin page on ``/sources.html`` clicks **Sync sources**;
|
||||
the page's OWN 2 s poll renders the import span's ``#sync-label``
|
||||
as the phase-128/129 PARTS — the main part
|
||||
``Syncing… chunkprog/big.md``, the pinned count part ``(1/2)``,
|
||||
``Syncing… chunkprog/big.md``, the pinned phase-193 count part
|
||||
``1 remaining`` (the big file is the first of two — N = total − done),
|
||||
and the VISIBLE chunk part matching ``· chunk n/m`` with the total
|
||||
== the expected chunk count (the big file's span is ~45 s under
|
||||
the slow proxy — the poll cannot miss it) — with the button
|
||||
@@ -903,14 +912,14 @@ def test_label_shows_the_chunk_part(
|
||||
_JS_IMPORT_CHUNK_SNAPSHOT, arg=n_chunks, timeout=SETTLE_TIMEOUT_MS
|
||||
).json_value()
|
||||
assert snap["mainText"] == f"Syncing… {SOURCE}/big.md", snap
|
||||
assert snap["countText"] == "(1/2)", snap
|
||||
assert snap["countText"] == "1 remaining", snap
|
||||
m = CHUNK_PART_RE.match(snap["chunkText"])
|
||||
assert m, snap
|
||||
assert int(m.group(2)) == n_chunks, snap # the TRUE chunk count, not a batch count
|
||||
assert int(m.group(1)) >= 1, snap
|
||||
# A4: the untruncated concatenation rides the title AND the
|
||||
# aria-live announcer (the "· chunk" part included).
|
||||
full = f"Syncing… {SOURCE}/big.md (1/2) {snap['chunkText']}"
|
||||
full = f"Syncing… {SOURCE}/big.md 1 remaining {snap['chunkText']}"
|
||||
assert snap["title"] == full and "· chunk" in snap["title"], snap
|
||||
# The box IS the announcer (phase 150) — it carries the same full
|
||||
# string the title does.
|
||||
|
||||
Reference in New Issue
Block a user