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:
@@ -73,7 +73,7 @@
|
||||
* disabled, title cleared) plus startUploadPolling(): a 2 s poll
|
||||
* of GET /api/git-sources/upload/status that renders the bare
|
||||
* "Processing…" label for the WHOLE run (phase 90, A2 — the run
|
||||
* is unpack + register only: no file, no "(n/m)" counts, no
|
||||
* is unpack + register only: no file, no "N remaining" count, no
|
||||
* title) and settles it: success → the ready-for-sync line
|
||||
* ("Uploaded <name> (<stamp>) — press Sync sources to import
|
||||
* it." — the phase-154 completion stamp, fmtUploadStamp off the
|
||||
@@ -1651,7 +1651,7 @@ export async function mount(root) {
|
||||
* title cleared) + startUploadPolling() own it — a 2 s poll of
|
||||
* GET /api/git-sources/upload/status that renders the bare
|
||||
* "Processing…" label for the WHOLE run (phase 90, A2 — the unpack
|
||||
* has no file-level progress: no file, no "(n/m)" counts, no title)
|
||||
* has no file-level progress: no file, no "N remaining" count, no title)
|
||||
* and settles it: success → the ready-for-sync line ("Uploaded
|
||||
* <name> (<stamp>) — press Sync sources to import it." — the
|
||||
* phase-154 completion stamp, fmtUploadStamp off the status's
|
||||
@@ -1786,7 +1786,7 @@ export async function mount(root) {
|
||||
* poll OWNS the button). Each tick fetches
|
||||
* GET /api/git-sources/upload/status: running → the bare
|
||||
* "Processing…" label for the whole run (phase 90, A2 — the unpack
|
||||
* has no file-level progress: no file, no "(n/m)" counts, the title
|
||||
* has no file-level progress: no file, no "N remaining" count, the title
|
||||
* stays clear) + reschedule; success → stop + the ready-for-sync
|
||||
* result line + the announcement + the row reload (NO toast — it
|
||||
* fired at the 202, A2); failed → stop + the sanitized server error
|
||||
|
||||
+33
-14
@@ -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).
|
||||
|
||||
@@ -2898,14 +2898,16 @@ a.answer-image:hover { text-decoration: underline; } /* flat — the caption und
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.sync-btn .sync-icon.is-spinning { animation: none; }
|
||||
}
|
||||
/* Phase 64: the live-file label ("Syncing… <file> (n/m)" / "Importing
|
||||
<file> (n/m)") — a long source/relative/path ellipsizes inside the
|
||||
/* Phase 64: the live-file label ("Syncing… <file> N remaining" /
|
||||
"Importing <file> N remaining" — phase 193: the file count is the
|
||||
distance to the end, `total - done`, hidden on the final file) — a
|
||||
long source/relative/path ellipsizes inside the
|
||||
pill; the full path lives in the button title + the #sync-progress
|
||||
box (the aria-live announcer — phase 150: the SOLE sync text
|
||||
surface). Phase 128 (task 02): the label is a FLEX ROW
|
||||
of parts — .sync-label-main (the prefix + file/folder: min-width
|
||||
0, the shrinkable item whose ellipsis engages) + .sync-label-count
|
||||
(the pinned "(n/m)": flex 0 0 auto, NEVER clipped — the
|
||||
(the pinned "N remaining": flex 0 0 auto, NEVER clipped — the
|
||||
.sync-progress-text mono language, a position reads as data). Phase 129
|
||||
(task 04): a THIRD pinned part — .sync-label-chunk (the "· chunk
|
||||
n/m" embed-batch position of the in-flight file, the same pinned
|
||||
@@ -2925,7 +2927,7 @@ a.answer-image:hover { text-decoration: underline; } /* flat — the caption und
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sync-label-count {
|
||||
flex: 0 0 auto; /* NEVER clipped — the (n/m) position is the point */
|
||||
flex: 0 0 auto; /* NEVER clipped — the N remaining count is the point */
|
||||
white-space: nowrap;
|
||||
font-family: var(--mono); /* the .sync-progress-text mono language (position reads as data) */
|
||||
}
|
||||
|
||||
+4
-2
@@ -490,7 +490,9 @@
|
||||
</p>
|
||||
</div>
|
||||
<!-- Phase 149 (task 01): the sync detail box — the full UNTRUNCATED live
|
||||
label ("Syncing… <source/rel/path> (n/m) · chunk n/m" — phase 152
|
||||
label ("Syncing… <source/rel/path> N remaining · chunk n/m" —
|
||||
phase 193: the file count counts DOWN to the end and hides on
|
||||
the last file; phase 152
|
||||
appends "· image n/m" while a PDF's vision span is in flight)
|
||||
in a bordered panel under the button row. Phase 150 (task 01) folded the
|
||||
announcer in: the previous live-region span beside it is retired,
|
||||
@@ -774,7 +776,7 @@
|
||||
inline. Phase 90 (owner-locked A1/A2/A3): the background
|
||||
run is UNPACK + REGISTER ONLY — no model check, no import,
|
||||
no overview refresh — so the processing state covers
|
||||
unpack only (no file, no "(n/m)" counts, no title) and
|
||||
unpack only (no file, no "N remaining" count, no title) and
|
||||
the success line points at the next step: "Uploaded
|
||||
<name> — press Sync sources to import it." The scan is
|
||||
the RAG page's Sync button's job (edit the source's
|
||||
|
||||
Reference in New Issue
Block a user