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
+33 -14
View File
@@ -160,7 +160,8 @@
* <source/folder> (n/m)" (the folder part omitted while
* current_summary is null — the phase's first poll), anything else
* (the null prelude, "import") → today's "Syncing… <file> (n/m)"
* byte-identical. The UPLOAD job (its status has no phase) keeps the
* byte-identical (the count part's TEXT is `N remaining` — the distance
* to the end — since phase 193). The UPLOAD job (its status has no phase) keeps the
* bare label. The untruncated label rides the button title (now set
* for EVERY running label, not just file labels) + #sync-progress as
* before (A4).
@@ -309,7 +310,8 @@
* Phase 128 (task 01) — the sync label becomes PARTS: the running
* label's string is split into a shrinks-to-fit MAIN part (prefix +
* the file/folder path — the part CSS may ellipsize) and a pinned
* COUNT part ("(n/m)" — the position, NEVER clipped), so on a phone
* COUNT part ("(n/m)" — the position, NEVER clipped — the text is
* `N remaining` since phase 193), so on a phone
* the file path ellipsizes to fit while the count stays on-screen
* (the phase-64 bug: with max-width lifted, the inline-block sized to
* its full content and the count ran off-screen). #sync-label now
@@ -332,7 +334,8 @@
* the initSyncButton re-attach — goes through setSyncLabel(main)
* (count hidden). The full-string shapes are byte-identical to the
* pre-task labels ("Syncing…", "Syncing… <file>",
* "Syncing… <file> (n/m)", "Importing", "Writing KB overview…",
* "Syncing… <file> (n/m)" — `N remaining` since phase 193, "Importing",
* "Writing KB overview…",
* "Summarizing folders… <folder> (n/m)"); textContent only — no HTML
* strings (the house rule).
*
@@ -747,7 +750,7 @@ export async function mount(root) {
* tree, in order (startSyncPolling):
* 1. sync running → the phase-aware label (Phase 98 task 02, D2)
* — the null-phase prelude + "import" →
* "Syncing… <file> (n/m)" — bare "Syncing…"
* "Syncing… <file> N remaining" — bare "Syncing…"
* until the import's first file (clone/pull,
* A4, byte-identical); "overview" → "Writing KB
* overview…"; "summaries" → "Summarizing
@@ -813,7 +816,8 @@ export async function mount(root) {
* Elements: #sync-btn (the button), #sync-label (the text —
* Phase 128 (task 01): the PARTS — .sync-label-main, the
* shrinks-to-fit prefix + file/folder, + .sync-label-count, the
* pinned "(n/m)" position (hidden when absent); phase 129 (task
* pinned count — the phase-193 "N remaining" (hidden when absent);
* phase 129 (task
* 04): + .sync-label-chunk, the pinned "· chunk n/m" embed-batch
* position of the in-flight file (hidden in every state but the
* import phase after the file's first batch); phase 152 (task 03):
@@ -917,7 +921,7 @@ export async function mount(root) {
* mainText lands in the MAIN part (.sync-label-main — the
* shrinks-to-fit prefix + file/folder, the part CSS ellipsizes);
* countText lands in the COUNT part (.sync-label-count — the pinned
* "(n/m)" position, NEVER clipped); chunkText (phase 129) lands in
* phase-193 "N remaining", NEVER clipped); chunkText (phase 129) lands in
* the CHUNK part (.sync-label-chunk — the pinned "· chunk n/m"
* embed-batch position of the in-flight file); imageText (phase
* 152) is the in-flight PDF's "· image n/m" vision-image position —
@@ -1034,9 +1038,20 @@ export async function mount(root) {
* BARE prefix shows during the clone/pull (sync), before any file
* is indexed. Phase 90: the upload run is unpack + register only
* (no scan), so its status never carries a file or counts — the
* "Importing" label is always the bare one. The counts — the PINNED
* count part, never clipped (Phase 128) — appear only once the
* import has started (total > 0). The builders return the PARTS
* "Importing" label is always the bare one — and when an upload
* status DOES carry counts the upload job shares this builder and
* the shape (phase 193, A2: the upload's files_total is the
* pre-walk count, the phase-98 contract — the same subtraction
* semantics). The count — the PINNED count part, never clipped
* (Phase 128) — is the phase-193 COUNT-DOWN part (the owner's
* "how many files are left, not how many files out of the
* total"): `N remaining` with N = total - done, shown while
* N >= 1 and HIDDEN at the final file (N == 0 — the main part
* names it; a "0 remaining" next to the last file's name is the
* stale-looking edge); plain integer, no thousands separator. The
* total > 0 guard stays (the phase-64 pre-walk contract — no count
* part before the walk knows the total). The builders return the
* PARTS
* ({ main, count }); setSyncLabel writes them (the untruncated full
* string — main + " " + count — rides the button title +
* #sync-progress, A4). Phase 98 (task 02): the SYNC job's
@@ -1053,7 +1068,7 @@ export async function mount(root) {
function fmtSyncLabel(kind, currentFile, done, total) {
const prefix = kind === "upload" ? "Importing" : "Syncing…";
const main = currentFile ? `${prefix} ${currentFile}` : prefix;
const count = total > 0 ? `(${done}/${total})` : "";
const count = total > 0 && total - done > 0 ? `${total - done} remaining` : "";
return { main, count };
}
@@ -1069,8 +1084,11 @@ export async function mount(root) {
* empty (the summaries phase's own (n/m) is its position — the
* status's chunk + image fields are cleared at this transition);
* anything else (the null prelude, "import") → the phase-64
* fmtSyncLabel parts, byte-identical (same builder, same fields,
* same order) + the phase-129 CHUNK part: "· chunk n/m" ONLY while
* fmtSyncLabel parts (same builder, same fields, same order —
* since phase 193 its count part reads `N remaining`, N =
* total - done, hidden at the final file; the summaries (n/m)
* folder position and the chunk/image parts below stay as they
* are) + the phase-129 CHUNK part: "· chunk n/m" ONLY while
* chunks_total > 0 (a file's embed batch is in flight — n the
* 1-based first chunk of the batch, m the file's total content
* chunks, batch granularity), empty before the first batch (0/0) so
@@ -1099,8 +1117,9 @@ export async function mount(root) {
image: "", // the image position belongs to the import phase only
};
}
// null phase (the prelude) or "import" — today's byte-identical
// sync label (the phase-64 contract, unchanged) + the phase-129
// null phase (the prelude) or "import" — the phase-64 label
// shape (since phase 193 its count part reads `N remaining`) +
// the phase-129
// chunk part (hidden before the in-flight file's first batch) +
// the phase-152 image part (hidden before the in-flight PDF's
// first vision describe call).