phase: 193_sync_remaining_count
Build and Push Containers / build-and-push-app (push) Successful in 2m3s
Build and Push Containers / build-and-push-db (push) Successful in 16s

**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:
2026-10-19 17:30:03 -04:00
parent fb17d15fbe
commit ae080ac7f3
36 changed files with 2111 additions and 174 deletions
+172 -29
View File
@@ -79,8 +79,9 @@ its own (n/m) is its position, idle / settle). setSyncLabel(mainText,
countText, chunkText) lands the part under the same present/absent
contract as the count part, and the UNTRUNCATED full label — main +
" " + count + " " + chunk — rides the button title + #sync-progress
(A4 — the in-flight import reads "Syncing… <file> (2/3) · chunk
14/38"). The part keeps the pinned mono CSS (flex 0 0 auto, nowrap,
(A4 — the in-flight import reads "Syncing… <file> 1 remaining · chunk
14/38" — phase 193 counts the files DOWN). The part keeps the pinned
mono CSS (flex 0 0 auto, nowrap,
--mono, the rule right after .sync-label-count). A node harness (the
test-document-viewer pattern) behaviorally pins the rendered parts +
the full title across every label state.
@@ -141,7 +142,8 @@ the 202/409 click stay bare). setSyncLabel(mainText, countText =
"", chunkText = "", running = false, imageText = "") appends the
image part LAST to the composed full string — so the box, the
button title, and the running aria-label all carry
"Syncing… <file> (2/3) · chunk 14/38 · image 3/15". The image part
"Syncing… <file> 1 remaining · chunk 14/38 · image 3/15" (phase 193).
The image part
gets NO span of its own (the running button is the spinner — its
parts are hidden, phase 149): it rides ONLY the composed string,
while the chunk part keeps its .sync-label-chunk span; the two
@@ -189,6 +191,19 @@ app/ no-touch regression guard). The qualifier's byte-identity
pinned in test_sync_button.py on the shared fixed clock; the four
shapes and the fmtUploadStamp behavior (finished / started /
missing / garbage) ride the task-02 pins below.
Phase 193 (task 01, D1) re-points the running file-count part: the
fmtSyncLabel COUNT part is the COUNT-DOWN `N remaining` (N =
total - done) — shown while N >= 1, HIDDEN at the final file (N == 0:
the main part names the file in flight — a "0 remaining" next to the
last file's name is the stale-looking edge the owner's complaint is
about), the total > 0 pre-walk guard KEPT, plain integer (no thousands
separator); the UPLOAD job shares the builder and the shape (A2). The
node harness rows that carried "(2/3)" now carry "1 remaining" (the
same 2/3 fixture — the part renders the distance to the end), the
harness gains the final-file row (the count part hidden), and the
summaries phase's own (n/m) folder position, the "· chunk n/m" /
"· image n/m" parts and the terminal counts line stay byte-identical.
"""
from __future__ import annotations
@@ -316,6 +331,19 @@ def _tick(js: str) -> str:
return js[tick:end]
def _doc_above(js: str, name: str) -> str:
"""The block comment immediately above `function <name>` (the
builder's docstring — _fn starts AT the keyword, so the lineage
paragraphs need this slice)."""
i = js.find(f"function {name}")
assert i != -1, f"{name} must be defined in sources.js"
start = js.rfind("/*", 0, i)
assert start != -1, f"{name} must carry a docstring"
end = js.find("*/", start)
assert start < end < i, f"{name}'s docstring must sit above the function"
return js[start:end]
# ---------- fmtSyncLabel: the live-file label PARTS contract ----------
@@ -328,9 +356,10 @@ def test_fmt_sync_label_returns_the_parts_contract() -> None:
prefix shows during clone/pull, before any file is indexed — A4;
phase 90: the upload run never carries a file or counts, so its
label is always the bare "Importing"); the COUNT part — only when
total > 0 (the import has started), NO join space (the space is
the CSS gap between the parts — the full string's space is
re-joined by setSyncLabel for the title / announcer, A4). File
total > 0 (the import has started) — is the phase-193 COUNT-DOWN
`N remaining` (N = total - done), NO join space of its own (the
space between the parts is the CSS gap — the full string's space
is re-joined by setSyncLabel for the title / announcer, A4). File
before counts in the full-string order."""
body = _fn(_js(), "fmtSyncLabel")
assert "function fmtSyncLabel(kind, currentFile, done, total)" in body
@@ -344,20 +373,95 @@ def test_fmt_sync_label_returns_the_parts_contract() -> None:
# the MAIN part: the file appended only when present
assert "currentFile ?" in body
assert "`${prefix} ${currentFile}`" in body
# the COUNT part: only when the import has started (total > 0),
# no join space (the parts are joined by setSyncLabel, A4)
# the COUNT part (phase 193, D1): only when the import has
# started (the kept total > 0 pre-walk guard) AND the run is not
# on its final file (total - done > 0), no leading join space
# (the parts are joined by setSyncLabel, A4)
assert "total > 0" in body
assert "`(${done}/${total})`" in body
assert "` (${done}/${total})`" not in body, ("no space in the count part")
assert "`${total - done} remaining`" in body
assert "(${done}/${total})" not in body, (
"the pre-phase (done/total) shape is gone (phase 193)"
)
i_file = body.find("currentFile ?")
i_counts = body.find("total > 0")
assert -1 < i_file < i_counts, "the file lands before the counts"
# the parts return (the byte-identical full strings hold when
# setSyncLabel joins them: "Syncing…", "Syncing… <file>",
# "Syncing… <file> (n/m)", "Importing")
# the parts return (the full strings hold when setSyncLabel joins
# them: "Syncing…", "Syncing… <file>",
# "Syncing… <file> N remaining", "Importing")
assert "return { main, count }" in body
def test_fmt_sync_label_count_part_is_the_n_remaining_part() -> None:
"""Phase 193 (task 01, D1): the running file-count part counts
DOWN. The fmtSyncLabel count expression is pinned VERBATIM — the
kept `total > 0` pre-walk guard (phase 64: no count part before
the walk knows the total) AND the new `total - done > 0` guard
(the part hides at the final file, ASSUMPTION A1) around the
plain-integer `${total - done} remaining` shape (no thousands
separator — the existing count part's house style). The docstring
names the new part (N = total - done, shown while N >= 1), the
hidden-at-the-final-file edge (N == 0 — the main part names the
file in flight; a "0 remaining" next to the last file's name is
the stale-looking edge), and the upload job sharing the builder
and the shape (A2)."""
js = _js()
body = _fn(js, "fmtSyncLabel")
# the exact expression, byte-for-byte (both guards + the shape +
# the empty-string else)
assert (
"const count = total > 0 && total - done > 0"
" ? `${total - done} remaining` : \"\";"
) in body, "the count expression is the D1 shape"
assert "total > 0 &&" in body, "the total > 0 pre-walk guard stays"
assert "total - done > 0" in body, (
"the part hides at the final file (N == 0, A1)"
)
# plain integer — no separator/format helper joins the number
assert "toLocaleString" not in body and ".toFixed" not in body, (
"plain integer, no thousands separator (the house label style)"
)
doc = _doc_above(js, "fmtSyncLabel")
assert "`N remaining`" in doc and "N = total - done" in doc, (
"the docstring describes the new part"
)
assert "N >= 1" in doc, "shown while N >= 1 (A1)"
assert "HIDDEN at the final file" in doc and "N == 0" in doc, (
"the hidden-at-final-file edge is named in the docstring (A1)"
)
assert "thousands separator" in doc, "the plain-integer clause is named"
assert "pre-walk" in doc, "the phase-64 total > 0 contract is named"
assert "shares this builder" in doc, (
"the upload job sharing the builder and the shape is named (A2)"
)
def test_remaining_part_leaves_the_other_parts_byte_identical() -> None:
"""Phase 193 (task 01): the COUNT-DOWN moved exactly ONE part.
The fmtSyncPhaseLabel import fall-through still builds the
phase-129 "· chunk n/m" and the phase-152 "· image n/m" parts
from the status's own fields, the summaries branch still emits
its FOLDER position `(${summaries_done}/${summaries_total})`,
the bare fmtSyncLabel builder stays free of all three (each part
lives only in its own builder), and the terminal counts line
(fmtSyncResult — a different surface) never grows the word."""
js = _js()
phase = _fn(js, "fmtSyncPhaseLabel")
assert "`· chunk ${status.current_chunk}/${status.chunks_total}`" in phase
assert "`· image ${status.images_done}/${status.images_total}`" in phase
assert (
"count: `(${status.summaries_done}/${status.summaries_total})`"
) in phase, "the summaries folder position keeps its (n/m) shape"
bare = _fn(js, "fmtSyncLabel")
for part in ("chunk", "image", "summaries"):
assert part not in bare, (
f"fmtSyncLabel stays {part}-free — the part lives only in its own builder"
)
assert "remaining" not in _fn(js, "fmtSyncResult"), (
"the terminal result line is untouched (the running count part"
" is the one change)"
)
def test_set_sync_label_is_the_single_parts_writer() -> None:
"""setSyncLabel(mainText, countText = "", chunkText = "",
running = false, imageText = "") — Phase 128 (task 01) + Phase
@@ -374,7 +478,7 @@ def test_set_sync_label_is_the_single_parts_writer() -> None:
(countText ? " " + countText : "") + (chunkText ? " " + chunkText
: "") + (imageText ? " " + imageText : "") — rides the button
title (A4 — the screen reader hears the
whole "Syncing… <file> (2/3) · chunk 14/38 · image 3/15" there,
whole "Syncing… <file> 1 remaining · chunk 14/38 · image 3/15" there,
and in the box — see below). The image part (phase 152) gets NO
span of its own (the running button is the spinner — its parts
are hidden, phase 149): it rides only the composed full string.
@@ -868,6 +972,15 @@ enterSyncRunningState("sync", "src/a/b.md", 2, 3, {
current_chunk: 0, chunks_total: 0,
});
show("import-prebatch");
// 2b. Phase 193 (task 01, A1): the FINAL file (files_done ==
// files_total) — the count part HIDES (the main part names the
// file in flight; a "0 remaining" next to it is the stale-looking
// edge):
enterSyncRunningState("sync", "src/a/b.md", 3, 3, {
phase: "import", current_file: "src/a/b.md", files_done: 3, files_total: 3,
current_chunk: 0, chunks_total: 0,
});
show("import-final");
// 3. the prelude (null phase, no file yet):
enterSyncRunningState("sync", null, 0, 0, {
phase: null, current_file: null, files_done: 0, files_total: 0,
@@ -957,9 +1070,13 @@ test-document-viewer pattern). The pinned "· chunk n/m" part (the
* import, batch in flight (chunks_total 38 > 0): the chunk part
shows "· chunk 14/38" and the UNTRUNCATED title + #sync-progress
(the sole live region) read exactly
"Syncing… src/a/b.md (2/3) · chunk 14/38" (A4);
"Syncing… src/a/b.md 1 remaining · chunk 14/38" (A4 — phase
193: the 2/3 fixture now renders the distance to the end);
* import before the first batch (0/0): the chunk part hides and
the label stays "Syncing… src/a/b.md (2/3)";
the label stays "Syncing… src/a/b.md 1 remaining";
* import on the FINAL file (phase 193, A1: files_done ==
files_total): the COUNT part hides (no "0 remaining") — the
main part alone names the file in flight;
* every OTHER state — prelude (null phase), the 202/409 click (no
status), the upload run (no status, D2), overview, summaries (its
own (n/m) is its position), the idle settle — leaves the chunk
@@ -982,24 +1099,39 @@ test-document-viewer pattern). The pinned "· chunk n/m" part (the
# untruncated label rides the title + the announcer (A4).
row = by_tag["import-inflight"]
assert row[3] == "Syncing… src/a/b.md", row
assert row[4] == "(2/3)" and row[5] is False, row
assert row[4] == "1 remaining" and row[5] is False, (
"phase 193 (D1): the count part is the count-DOWN (3 − 2 = 1)"
)
assert row[6] == "· chunk 14/38" and row[7] is False, (
"the chunk part shows the batch position (1-based first chunk / file total)"
)
assert row[1] == "Syncing… src/a/b.md (2/3) · chunk 14/38", row[1]
assert row[1] == "Syncing… src/a/b.md 1 remaining · chunk 14/38", row[1]
assert row[2] == row[1], "the announcer hears the whole thing (A4)"
# 2. the import before the first batch (0/0) — the part hides, the
# label is the byte-identical phase-64 string.
# 2. the import before the first batch (0/0) — the chunk part
# hides, the label keeps its phase-64 shape with the phase-193
# count part.
row = by_tag["import-prebatch"]
assert row[1] == "Syncing… src/a/b.md (2/3)", row[1]
assert row[1] == "Syncing… src/a/b.md 1 remaining", row[1]
assert row[2] == row[1]
assert row[6] == "" and row[7] is True, row
# 2b. the FINAL file (N == 0) — the count part HIDES (phase 193,
# A1): no "0 remaining", the main part names the file, and
# the A4 full label is just the main part.
row = by_tag["import-final"]
assert row[3] == "Syncing… src/a/b.md", row
assert row[4] == "" and row[5] is True, (
"the count part hides at the final file (total - done == 0)"
)
assert row[1] == "Syncing… src/a/b.md", row[1]
assert row[2] == row[1]
# 3. every other RUNNING state — the chunk part is hidden +
# empty, the title is the byte-identical pre-phase-129 label,
# and the announcer (the box) hears the whole label (A4).
for tag, expected_title in (
("import-final", "Syncing… src/a/b.md"),
("prelude", "Syncing…"),
("click", "Syncing…"),
("upload", "Importing"),
@@ -1029,7 +1161,15 @@ test-document-viewer pattern). The pinned "· chunk n/m" part (the
# upload job is NOT cancellable in this phase) and hidden +
# disabled after the real settle (settleSyncButton always hides
# it — every terminal + idle passes through it).
for tag in ("import-inflight", "import-prebatch", "prelude", "click", "overview", "summaries"):
for tag in (
"import-inflight",
"import-prebatch",
"import-final",
"prelude",
"click",
"overview",
"summaries",
):
row = by_tag[tag]
assert row[8] is False and row[9] is False, (
f"{tag}: the Cancel control must be shown + enabled while a sync run is in flight"
@@ -1053,14 +1193,15 @@ def test_image_part_renders_only_in_the_import_phase_under_node() -> None:
WITH the last chunk position still on the status (125/126):
the composed full string carries BOTH parts — the box, the
button title, and the running aria-label all read exactly
"Syncing… src/a/b.md (2/3) · chunk 125/126 · image 3/15"
(the owner's stuck label, now moving); the chunk part KEEPS
"Syncing… src/a/b.md 1 remaining · chunk 125/126 · image 3/15"
(the owner's stuck label, now moving; phase 193: the count part
is the count-DOWN); the chunk part KEEPS
its span ("· chunk 125/126" visible) and the image part gets
NO span of its own (it rides the composed string only — the
running button is the spinner, phase 149);
* import, a purely pictorial PDF (the chunk position never left
0/0) in its vision span: the image part shows ALONE —
"Syncing… src/a/b.md (2/3) · image 7/7" — and the chunk part
"Syncing… src/a/b.md 1 remaining · image 7/7" — and the chunk part
stays hidden + empty (the two parts are distinct and each
disappears independently on its own 0/0);
* every OTHER running state (the pre-batch import, the prelude,
@@ -1082,7 +1223,7 @@ def test_image_part_renders_only_in_the_import_phase_under_node() -> None:
# (the image part LAST, after the chunk part), on the box, the
# title, and the running aria-label (A4) — the chunk span
# keeps its own part.
full = "Syncing… src/a/b.md (2/3) · chunk 125/126 · image 3/15"
full = "Syncing… src/a/b.md 1 remaining · chunk 125/126 · image 3/15"
row = by_tag["import-image-span"]
assert row[1] == full, row[1]
assert row[2] == full, "the announcer (the box) hears the whole thing (A4)"
@@ -1095,7 +1236,7 @@ def test_image_part_renders_only_in_the_import_phase_under_node() -> None:
# 1c. the image part alone (the chunk position never left 0/0 —
# the two parts are independent).
row = by_tag["import-image-only"]
assert row[1] == "Syncing… src/a/b.md (2/3) · image 7/7", row[1]
assert row[1] == "Syncing… src/a/b.md 1 remaining · image 7/7", row[1]
assert row[2] == row[1] and row[14] == row[1], row
assert row[6] == "" and row[7] is True, (
"the chunk part stays hidden + empty (its own 0/0)"
@@ -1106,8 +1247,9 @@ def test_image_part_renders_only_in_the_import_phase_under_node() -> None:
# hides on its own 0/0 — the overview/summaries transitions
# clear the position server-side, the bare builders carry none).
for tag, expected_title in (
("import-inflight", "Syncing… src/a/b.md (2/3) · chunk 14/38"),
("import-prebatch", "Syncing… src/a/b.md (2/3)"),
("import-inflight", "Syncing… src/a/b.md 1 remaining · chunk 14/38"),
("import-prebatch", "Syncing… src/a/b.md 1 remaining"),
("import-final", "Syncing… src/a/b.md"),
("prelude", "Syncing…"),
("click", "Syncing…"),
("upload", "Importing"),
@@ -1176,6 +1318,7 @@ def test_progress_box_and_running_button_contract_under_node() -> None:
for tag in (
"import-inflight",
"import-prebatch",
"import-final",
"prelude",
"click",
"upload",
+4 -3
View File
@@ -268,8 +268,9 @@ def test_sync_button_has_icon_and_label_parts() -> None:
Phase 128 (task 01): #sync-label is PARTS — the MAIN span
(.sync-label-main) carries the idle text (the pre-JS label renders
from the static markup; the part CSS may ellipsize it), the
COUNT span (.sync-label-count — the pinned "(n/m)" position,
never clipped) ships hidden + empty (there is no count at idle),
COUNT span (.sync-label-count — the pinned phase-193
"N remaining" position, never clipped) ships hidden + empty
(there is no count at idle),
and — phase 129 (task 04) — the CHUNK span (.sync-label-chunk —
the pinned "· chunk n/m" embed-batch position) ships hidden +
empty too (no chunk position at idle — the part renders only
@@ -516,7 +517,7 @@ def test_fmt_sync_phase_label_image_part_is_pinned_source_level() -> None:
`running` flag);
* ``setSyncLabel``'s composed ``full`` string appends the image
part LAST (after the chunk part) — so a status carrying both
positions reads exactly "Syncing… <file> (2540/2540) · chunk
positions reads exactly "Syncing… <file> 1 remaining · chunk
125/126 · image 3/15" on the box, the title, and the running
aria-label (byte-for-byte; the docstring's worked example
carries the shape).