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:
@@ -0,0 +1,55 @@
|
||||
# Phase 193 — The sync label counts DOWN: "N remaining"
|
||||
|
||||
**Source:** `TODO.md` L6 — "The sync status should show how many files are left to sync, not how many files out of the total."
|
||||
**Story:** n/a (owner directive)
|
||||
**Depends on:** none functionally (builds on the completed phase-64/98/128/129/152 sync-label lineage).
|
||||
|
||||
## Evidence (diagnosed from the code, read this not the chat)
|
||||
|
||||
1. **The running label counts UP.** `frontend/assets/sources.js` `fmtSyncLabel` (L1053–1058): the import phase and the upload job render `count = total > 0 ? `(${done}/${total})` : ""` — `Syncing… notes/a.md (1234/27000)`. The owner wants the distance to the end, not the position in the whole: `25766 remaining`.
|
||||
2. **The data already exists.** `SyncStatus.files_done` / `files_total` (the phase-64 payload — the running label's only inputs) carry everything: `N = total − done` is a subtraction, no server change, no new field, no new poll. The `#sync-progress` line (the phase-152 A4: the untruncated full label rides the button title + `#sync-progress` via `setSyncLabel`) gets the new part through the SAME builder — nothing to change there.
|
||||
3. **The other count parts are different animals — they stay.** `· chunk n/m` (phase 129 — one file's embed batch), `· image n/m` (phase 152 — one PDF's vision span), and the summaries phase's `(n/m)` (phase 98 — the FOLDER position, always emitted, `fmtSyncPhaseLabel` L1089+) are per-file / per-folder position parts, not "files of the total". The terminal result line (`counts_line` — "N added · M updated …") is a different surface and is untouched.
|
||||
|
||||
## Design (shared by all tasks — the executor reads this, not the chat)
|
||||
|
||||
- **D1 — the part (task 01).** `fmtSyncLabel`'s `count` becomes `total > 0 && total - done > 0 ? `${total - done} remaining` : ""` — the owner-confirmed shape (ASSUMPTION A1, confirmed 2026-10-18):
|
||||
- `N remaining` shows while `N ≥ 1`; at the final file (`N == 0`) the part is HIDDEN — the main part names the file in flight, and a "0 remaining" next to the last file's name is the stale-looking edge the owner's complaint is about;
|
||||
- plain integer (no thousands separator — the existing count part is plain; the house label style);
|
||||
- the POSITION is unchanged (main → count → chunk → image — the phase-128 parts rework order);
|
||||
- the UPLOAD job shares the builder and the format (owner-confirmed A2 — `Importing <file> N remaining`; the upload's `files_total` is the pre-walk count, the phase-98 contract — the same subtraction semantics);
|
||||
- `total === 0` (the pre-walk null total, the phase-64 contract) → no count part (unchanged);
|
||||
- the chunk / image parts and the summaries-phase folder position are UNTOUCHED (byte-identical);
|
||||
- the docstring (L1044–1058 region) is updated to the new part (the phase-64/98 lineage paragraph keeps its history, the `(done/total)` description becomes the `N remaining` one).
|
||||
- **D2 — the pinned suites move (task 02).** The running-label count pins retarget to the new part; the terminal-line pins stay unedited. The definitive list is found by `grep -rn "Syncing…\|Importing" tests/e2e/*.py | grep -E "\\\\d|/\{N|[0-9]+\)"` at execution time (the known pins: `test_sync_label_mobile.py` `IMPORT_FULL_RE` L226 + the docstring L93/L213; `test_sync_upload_progress.py` L653/L667/L850).
|
||||
|
||||
## ASSUMPTIONS (owner-confirmed 2026-10-18)
|
||||
- **A1:** `N remaining` while `N ≥ 1`, hidden at `N == 0` (the final file — the main part names it).
|
||||
- **A2:** the upload job shares the format (one builder, one shape).
|
||||
|
||||
## Context
|
||||
- `frontend/assets/sources.js` — `fmtSyncLabel` (L1053–1058), `fmtSyncPhaseLabel` (L1089+, the phase-aware wrapper: overview / summaries / import), `setSyncLabel` (the A4 full-label placement — the button title + `#sync-progress`), the `counts_line` helper (the terminal result line — UNTOUCHED); `app/schemas.py` `SyncStatus` (`files_done` / `files_total` — the payload, UNTOUCHED); `app/api/sync.py` (the status endpoint — UNTOUCHED).
|
||||
- `tests/e2e/test_sync_label_mobile.py`, `tests/e2e/test_sync_upload_progress.py`, `tests/e2e/test_sync_chunk_progress.py`, `tests/e2e/test_sync_summary_visibility.py` (the phase-64/98/128/129/152 label suites — the count-part pins; the chunk/image/summaries pins stay), `tests/e2e/test_sync_git_freeze.py` + `test_sync_session_reset.py` + `test_fast_path_deferred_content.py` (the `counts_line` terminal pins — unedited).
|
||||
|
||||
## Tasks
|
||||
1. `01_remaining_label.md` — the `fmtSyncLabel` count part becomes `N remaining` (the D1 spec), the docstring update, the JS marker pin.
|
||||
2. `02_update_pinned_e2e.md` — the running-label count pins retarget to the new part (the D2 list); the full suite green.
|
||||
3. `03_e2e_and_gates.md` — the phase's own suite (the count-down through the real UI), the full gates, the docs pass, the atomic commit.
|
||||
|
||||
## Testing & Quality
|
||||
- **Unit (JS marker pin):** the new count expression (`total - done > 0` guard, the plain-integer shape, the `total > 0` guard kept, the upload kind sharing the builder — the marker names the expression).
|
||||
- **E2E (regression, task 02):** the retargeted count pins + the chunk / image / summaries pins green; the terminal `counts_line` pins green UNEDITED.
|
||||
- **E2E (phase, AGENTS rule 4, task 03):** `tests/e2e/test_sync_remaining_count.py`, run in isolation — the count-down arm (the part decreases across polls; hidden at the final file; the chunk part coexists), the upload arm (the shared format).
|
||||
- **Coverage:** **>90%** on `app/` (unchanged — the task is frontend; the app-side gate stays green).
|
||||
- **Lint/types:** `uv run ruff check . && uv run pyright`.
|
||||
|
||||
## Documentation & Consistency
|
||||
- Final pass reviews `README.md` and `AGENTS.md` in their entirety against the post-phase app and fixes every inconsistency found, pre-existing ones included.
|
||||
- `README.md`: the sync section's running-label description (the `(done/total)` example, if present) becomes the `N remaining` one; `AGENTS.md` — the phase-64 sync-label clause (if it names the count part) gains the new shape (task 03 — verify, do not add if absent).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] the running file-count part is `N remaining` (`N = total − done`, plain, shown while `N ≥ 1`, hidden at `N == 0`, `total === 0` → no part) — the marker pin + the E2E count-down arm.
|
||||
- [ ] the upload label uses the same shape (the E2E upload arm); the chunk / image / summaries parts are byte-identical (the unedited pins green).
|
||||
- [ ] the terminal result line is byte-identical (the `counts_line` pins green unedited); the `#sync-progress` line carries the new part through the same builder (the A4 pin).
|
||||
- [ ] test suite green, coverage >90%; `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] no behavior change in completed phases (the server, the payload, the phases of the run all untouched — the change is the one label part).
|
||||
- [ ] one atomic Conventional Commits commit with `--no-gpg-sign`.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Task 01 — The `N remaining` count part (D1)
|
||||
|
||||
**Phase:** `193_sync_remaining_count` · **Source:** `TODO.md:7 — "The sync status should show how many files are left to sync, not how many files out of the total."`
|
||||
**Story:** n/a
|
||||
|
||||
## Objective
|
||||
The one-line change the owner asked for: `fmtSyncLabel`'s running file-count part renders the distance to the end (`N remaining`, `N = total − done`) instead of the position in the whole (`(done/total)`) — sync and upload alike (one builder), hidden at the final file, the pre-walk `total === 0` behavior unchanged, the chunk / image / summaries parts untouched.
|
||||
|
||||
## Work
|
||||
1. `frontend/assets/sources.js` — `fmtSyncLabel` (L1053–1058):
|
||||
- the `count` expression: `const count = total > 0 && total - done > 0 ? `${total - done} remaining` : "";` (the `total > 0` guard kept — the phase-64 pre-walk contract: no count part before the walk knows the total; the plain integer — no thousands separator, the existing part's style; the POSITION in the returned object is unchanged — the phase-128 parts order main → count → chunk → image is preserved by `fmtSyncPhaseLabel` which consumes `count` unchanged);
|
||||
- the docstring block above the function (L1044–1052) + the `fmtSyncPhaseLabel` docstring (L1060–1088): the `(done/total)` description becomes the `N remaining` one — the phase-64/98 lineage KEPT (the "count part" history), the new clause: `N = total − done`, shown while `N ≥ 1`, hidden at `N == 0` (the final file — the main part names it; a "0 remaining" next to the last file's name is the stale-looking edge), the upload job shares the builder and the shape (the phase-98 D2 "upload keeps the bare fmtSyncLabel parts" clause updated: still bare, now with the `N remaining` part);
|
||||
- `setSyncLabel` / `#sync-progress` placement: UNTOUCHED (the A4 full label rides the same builder — the new part flows through automatically);
|
||||
- the terminal `counts_line` helper: UNTOUCHED.
|
||||
2. `tests/unit/` — the JS marker pin (the sources-JS pin module — verify the name; the `test_save_chat_ui.py` convention):
|
||||
- the `fmtSyncLabel` count expression marker (the `total > 0 && total - done > 0` guard + the `` `${total - done} remaining` `` shape — the source marker asserts the exact expression);
|
||||
- the `total === 0` guard marker (no count part before the walk);
|
||||
- the "hidden at N == 0" clause marker (the docstring names the final-file edge);
|
||||
- the chunk / image / `fmtSyncPhaseLabel` parts UNTOUCHED marker (the three part builders' source is unchanged — the `· chunk n/m` / `· image n/m` / summaries `(n/m)` strings appear only in their own builders, not in `fmtSyncLabel`).
|
||||
3. Verify: `uv run pytest tests/unit/<sources-pin-module> -v` green; `uv run pytest` green; `uv run ruff check . && uv run pyright` clean.
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: the marker pins above (the expression is the whole change — the markers pin the shape).
|
||||
- E2E: task 02 (the retargeted pins) + task 03 (the phase arm).
|
||||
- Coverage: **>90%** on `app/` (unchanged — the task is frontend).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `fmtSyncLabel`'s count part is `` `${total - done} remaining` `` with the two guards (the marker pins).
|
||||
- [ ] the docstrings name the new part + the hidden-at-final-file edge + the upload sharing (the marker pins).
|
||||
- [ ] the chunk / image / summaries / terminal-line code is byte-identical (the unedited pins green).
|
||||
- [ ] no behavior change in completed work (the server and the payload are untouched).
|
||||
@@ -0,0 +1,25 @@
|
||||
# Task 02 — The pinned running-label count pins move to `N remaining` (D2)
|
||||
|
||||
**Phase:** `193_sync_remaining_count` · **Source:** `TODO.md:7`
|
||||
**Story:** n/a
|
||||
|
||||
## Objective
|
||||
The full suite stays green: every E2E pin that asserts the running file-count part retargets to the new shape; the chunk / image / summaries pins and the terminal `counts_line` pins stay UNEDITED.
|
||||
|
||||
## Work
|
||||
1. The definitive pin list — run at execution time: `grep -rn "Syncing…\|Importing" tests/e2e/*.py | grep -E "\\\\d|/\{N|[0-9]+\)"` — the known pins (verify each against the list):
|
||||
- `tests/e2e/test_sync_label_mobile.py` — `IMPORT_FULL_RE` (L226): `^Syncing… {FILE_RE} \([1-4]/4\)(?: · chunk \d+/\d+)?$` → `^Syncing… {FILE_RE} [1-4] remaining(?: · chunk \d+/\d+)?$` (a 4-file fixture: while file `i` is in flight `done = i − 1`, so `N = 4 − done ∈ {1, 2, 3, 4}` — `N == 0` never renders, the part is `[1-4] remaining`; the docstring lines L93/L213 updated to the new shape — the `IMPORT_MAIN_RE` (L217, the no-count mobile clip) is UNTOUCHED);
|
||||
- `tests/e2e/test_sync_upload_progress.py` — L653, L667, L850: `\(\d+/{N_FILES}\)` → `[0-9]+ remaining` (the same module's `N_FILES` fixture; the L667 arm keeps the ` · chunk \d+/\d+` tail; the L653/L850 arms are the bare label).
|
||||
2. Any ADDITIONAL pin the grep surfaces (a label regex with a count part in another suite — the phase-128/129/152 lineage: `test_sync_chunk_progress.py`, `test_sync_summary_visibility.py`, `test_sync_git_freeze.py`, `test_sync_session_reset.py` are the candidates — verify each: a pin on the RUNNING label's count part → retargeted; a pin on the CHUNK/IMAGE part, the summaries folder position, or the `counts_line` terminal line → UNTOUCHED).
|
||||
3. The module docstrings of the retargeted suites: the shape description updated (the `(n/m)` running-count clause → the `N remaining` clause, the phase-64/98 lineage KEPT, one sentence: phase 193 moved the part to the distance-to-end per the owner's directive).
|
||||
4. Verify: `uv run pytest tests/e2e/test_sync_label_mobile.py tests/e2e/test_sync_upload_progress.py -v --no-cov` green (plus every additional retargeted suite); `uv run pytest tests/e2e/test_sync_chunk_progress.py tests/e2e/test_sync_summary_visibility.py tests/e2e/test_sync_git_freeze.py tests/e2e/test_sync_session_reset.py -v --no-cov` green (the unedited chunk/image/terminal pins); `uv run pytest` (unit + integration) green.
|
||||
|
||||
## Testing & Quality
|
||||
- E2E (regression): the retargeted count pins green; the chunk / image / summaries / terminal pins green unedited.
|
||||
- Coverage: **>90%** on `app/` (unchanged).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] every running-label count pin in `tests/e2e/` asserts the `N remaining` shape (the grep list is closed — every hit accounted for: retargeted or documented-untouched).
|
||||
- [ ] the terminal `counts_line` pins and the chunk / image / summaries pins are green UNEDITED.
|
||||
- [ ] `uv run pytest` 100% passing (the full suite — no pin left asserting the old shape).
|
||||
- [ ] no behavior change in completed work (test-only + the task-01 label part).
|
||||
@@ -0,0 +1,34 @@
|
||||
# Task 03 — The phase E2E suite, the full gates, the docs pass, the commit
|
||||
|
||||
**Phase:** `193_sync_remaining_count` · **Source:** `TODO.md:7`
|
||||
**Story:** n/a
|
||||
|
||||
## Objective
|
||||
The dedicated Playwright suite proves the owner-visible contract — the label counts DOWN as the run progresses and goes quiet at the final file — run in isolation per AGENTS rule 4; the full gates pass; the docs are consistent; one atomic commit.
|
||||
|
||||
## Work
|
||||
1. `tests/e2e/test_sync_remaining_count.py` (NEW — the module docstring names the TODO item, the A1/A2 assumptions and the "server untouched" clause) — the module-app fixture pattern (the phase-64/98 E2E convention — verify in `tests/e2e/conftest.py` / the sibling sync suites):
|
||||
- the fixture: a small git source with N files (N ≥ 4, the phase-98 fixture convention — the `N_FILES` shape of `test_sync_upload_progress.py`).
|
||||
- **(a) the count-down arm:** start the sync through the RAG page's sync flow; capture `#sync-progress` (and the button title — the A4 full-label placement) across the run: while files are in flight the label matches `^Syncing… <file> [1-N] remaining` and the displayed `N` is NON-INCREASING across polls (the `total − done` shape — a strict count-down; the pin: the sequence of captured `N`s, when a new value appears, is smaller); the `· chunk n/m` part coexists when a batch is in flight (the phase-129 part, unbroken); when the walk reaches the final file the part is ABSENT (the main part names the file — `^Syncing… <last-file>$`, no count part — the A1 hidden edge); the run's terminal result line is the phase-64 shape (the `counts_line` helper — byte-identical, the terminal-line pin).
|
||||
- **(b) the upload arm (A2):** the upload job (the same fixture as an archive, the `test_sync_upload_progress.py` convention) → the label shows `Importing <file> [1-N] remaining` (the shared builder, the shared shape — the pin) and the chunk part coexists (the pin).
|
||||
- **(c) the pre-walk edge:** the first poll before the walk knows the total → NO count part (`^Syncing…$`, the phase-64 contract — the `total === 0` guard, owner-visible).
|
||||
2. Regression, run in isolation: `tests/e2e/test_sync_label_mobile.py`, `tests/e2e/test_sync_upload_progress.py` (the task-02 retargets), `tests/e2e/test_sync_chunk_progress.py`, `tests/e2e/test_sync_summary_visibility.py` (the chunk/image/summaries pins) — green.
|
||||
3. The full gates, in order:
|
||||
- `uv run pytest` (unit + integration) — 100% passing;
|
||||
- `uv run pytest --cov=app --cov-report=term-missing` — **>90%** on `app/`;
|
||||
- `uv run pytest tests/e2e/test_sync_remaining_count.py -v --no-cov` — green in isolation;
|
||||
- `uv run ruff check . && uv run pyright` — clean.
|
||||
4. Documentation pass (the phase file's Documentation section): `README.md` + `AGENTS.md` reviewed in their entirety against the post-phase app; the sync section's running-label example (the `(done/total)` shape, if present in `README.md`) becomes the `N remaining` one; `AGENTS.md` corrected only where a phase-64/98 clause names the running count part.
|
||||
5. Commit: one atomic, professional commit (Conventional Commits, `--no-gpg-sign`), e.g.
|
||||
`feat(sync): the running label counts down — "N remaining" (phase 193)` — the body names the one-line `fmtSyncLabel` change (sync + upload through the shared builder), the hidden-at-final-file edge, and the untouched chunk/image/summaries/terminal parts. `.agents/phases/todo/193_sync_remaining_count/` moves to `.agents/phases/complete/` in the SAME commit (AGENTS rule 8).
|
||||
|
||||
## Testing & Quality
|
||||
- E2E: the suite above, isolated; the regression suites green.
|
||||
- Coverage: **>90%** on `app/`.
|
||||
- Lint/types: clean.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `tests/e2e/test_sync_remaining_count.py` green in isolation — (a) the count-down (non-increasing `N` across polls, the chunk part coexisting, the part absent at the final file, the terminal line byte-identical); (b) the upload arm (`Importing <file> N remaining`); (c) the pre-walk edge (no count part before the total is known).
|
||||
- [ ] `uv run pytest` 100% passing; `uv run pytest --cov=app --cov-report=term-missing` >90%; `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] `README.md` / `AGENTS.md` consistent (the running-label shape).
|
||||
- [ ] the phase directory is in `.agents/phases/complete/`; one atomic commit with `--no-gpg-sign`.
|
||||
Reference in New Issue
Block a user