phase: 129_sync_chunk_progress
All green. Final report:
**Phase 129 final verification pass — 100% green** (previous work intact; this pass re-verified everything and fixed two pre-existing README inconsistencies)
- Verified on-spec: `LLMClient.embed` per-batch `on_progress` hook; importer `on_chunk_progress` plumbing (text/image/PDF branches; summary embed excluded); `SyncStatus.current_chunk/chunks_total` reset+clear contract; UI `.sync-label-chunk` pinned part with untruncated `title`/`#sync-result`
- `uv run pytest --cov=app -q`: exit 0, ~3041 passed, 0 failed, `app/` coverage **99%** (>90% gate)
- `uv run pytest tests/e2e/test_sync_chunk_progress.py -v --no-cov` (isolated, DB up): **2 passed** (endpoint chunk climb with true `chunk_document` total + UI `· chunk n/m` part)
- `uv run ruff check .`: all checks passed · `uv run pyright`: 0 errors, 0 warnings
- Completion criteria: all 5 ✅ (incl. no behavior change in completed phases — full suite green, CLI/chat no-op paths byte-identical)
- Docs: README.md fixed 2 pre-existing inconsistencies (upload section claimed "unpacks + scans immediately" — phase 90 made it unpack+register only, scan deferred to Sync; troubleshooting claimed "120s client timeout" — actual guard is the 300 s pre-token `TURN_TIMEOUT_MS`). Phase-129 label behavior already documented. AGENTS.md re-verified consistent — no changes
- Removed stray untracked `NOTES.md` (8-byte artifact); no live-infra changes; no commits — all changes left in working tree for the harness
- Next pending phase: `130_chat_answer_clusters`
This commit is contained in:
@@ -64,8 +64,9 @@ Contract under test:
|
||||
documents** (the upload unpacked + registered only) and the sync
|
||||
button settled idle with no error UI; clicking **Sync sources**
|
||||
then imports the uploaded row with the LIVE "Syncing… <file> (n/m)"
|
||||
label (both layers) and lands the counts ("N added", the catalog
|
||||
refreshes);
|
||||
label (both layers — the phase-129 pinned chunk part "· chunk n/m"
|
||||
rides the label + title while a file's embed batch is in flight)
|
||||
and lands the counts ("N added", the catalog refreshes);
|
||||
* **upload processing (phase 90 A2)**: the upload button shows the
|
||||
BARE "Processing…" for the whole background run (no file, no
|
||||
"(n/m)", no title — proven across a held first status GET); every
|
||||
@@ -75,9 +76,11 @@ Contract under test:
|
||||
result line points at the Sync button; the KB stays empty;
|
||||
* **sync live file (A4)**: a multi-file local source; clicking
|
||||
**Sync sources** on ``/sources.html`` shows "Syncing…" (bare, the
|
||||
pre-64 click state) then "Syncing… <file> (n/m)" (both layers),
|
||||
then the pre-64 success settle — "Synced HH:MM" + the counts result
|
||||
line — preserved, plus the file in the label;
|
||||
pre-64 click state) then "Syncing… <file> (n/m)" (both layers — the
|
||||
phase-129 pinned chunk part "· chunk n/m" rides the label + title
|
||||
while a file's embed batch is in flight), then the pre-64 success
|
||||
settle — "Synced HH:MM" + the counts result line — preserved, plus
|
||||
the file in the label;
|
||||
* **reload re-attach (A1 + A2, phase 90)**: starting an upload and
|
||||
reloading ``/git-sources.html`` (the sub-second run may still be in
|
||||
flight — the boot re-attach enters the bare Processing state — or
|
||||
@@ -607,20 +610,29 @@ def test_upload_toast_then_navigate_away(
|
||||
# tick — the file rides the MAIN part, "(n/m)" the pinned COUNT
|
||||
# span (phase 128: the space between them is the CSS gap, so the
|
||||
# rendered concatenation carries none — the FULL string with the
|
||||
# space rides the button title, A4).
|
||||
# space rides the button title, A4). Phase 129: the pinned CHUNK
|
||||
# part ("· chunk n/m" — the file's embed-batch position, batch
|
||||
# granularity) renders while a file's embed batch is in flight —
|
||||
# both the rendered label (no space — the CSS gap) and the title
|
||||
# (with the space, A4) carry it.
|
||||
expect(page.locator("#sync-label")).to_have_text(
|
||||
re.compile(rf"Syncing… {re.escape(UPLOAD_NAME)}/.+\.md\(\d+/{N_FILES}\)"),
|
||||
re.compile(
|
||||
rf"Syncing… {re.escape(UPLOAD_NAME)}/.+\.md\(\d+/{N_FILES}\)(?:· chunk \d+/\d+)?"
|
||||
),
|
||||
timeout=SETTLE_TIMEOUT_MS,
|
||||
)
|
||||
# Phase 128: the "(n/m)" is NEVER clipped — it sits in its own
|
||||
# visible count span, and the button title carries the full
|
||||
# untruncated string (main + " " + count, A4).
|
||||
# untruncated string (main + " " + count [+ " " + chunk, phase
|
||||
# 129], A4).
|
||||
expect(page.locator("#sync-label .sync-label-count")).to_be_visible(
|
||||
timeout=SETTLE_TIMEOUT_MS
|
||||
)
|
||||
expect(btn).to_have_attribute(
|
||||
"title",
|
||||
re.compile(rf"^Syncing… {re.escape(UPLOAD_NAME)}/.+\.md \(\d+/{N_FILES}\)$"),
|
||||
re.compile(
|
||||
rf"^Syncing… {re.escape(UPLOAD_NAME)}/.+\.md \(\d+/{N_FILES}\)(?: · chunk \d+/\d+)?$"
|
||||
),
|
||||
timeout=SETTLE_TIMEOUT_MS,
|
||||
)
|
||||
|
||||
@@ -791,20 +803,31 @@ def test_sync_live_file_label(
|
||||
# tick — the file rides the MAIN part, "(n/m)" the pinned COUNT
|
||||
# span (phase 128: the space between them is the CSS gap, so the
|
||||
# rendered concatenation carries none — the FULL string with the
|
||||
# space rides the button title, A4).
|
||||
# space rides the button title, A4). Phase 129: the pinned CHUNK
|
||||
# part ("· chunk n/m" — the file's embed-batch position, batch
|
||||
# granularity) renders while a file's embed batch is in flight —
|
||||
# both the rendered label (no space — the CSS gap) and the title
|
||||
# (with the space, A4) carry it.
|
||||
expect(page.locator("#sync-label")).to_have_text(
|
||||
re.compile(rf"Syncing… {re.escape(SYNC_SOURCE_DIR)}/.+\.md\(\d+/{N_FILES}\)"),
|
||||
re.compile(
|
||||
rf"Syncing… {re.escape(SYNC_SOURCE_DIR)}/.+\.md\(\d+/{N_FILES}\)(?:· chunk \d+/\d+)?"
|
||||
),
|
||||
timeout=SETTLE_TIMEOUT_MS,
|
||||
)
|
||||
# Phase 128: the "(n/m)" is NEVER clipped — it sits in its own
|
||||
# visible count span, and the button title carries the full
|
||||
# untruncated string (main + " " + count, A4).
|
||||
# untruncated string (main + " " + count [+ " " + chunk, phase
|
||||
# 129], A4).
|
||||
expect(page.locator("#sync-label .sync-label-count")).to_be_visible(
|
||||
timeout=SETTLE_TIMEOUT_MS
|
||||
)
|
||||
expect(btn).to_have_attribute(
|
||||
"title",
|
||||
re.compile(rf"^Syncing… {re.escape(SYNC_SOURCE_DIR)}/.+\.md \(\d+/{N_FILES}\)$"),
|
||||
re.compile(
|
||||
"^Syncing… "
|
||||
rf"{re.escape(SYNC_SOURCE_DIR)}/.+\.md \(\d+/{N_FILES}\)"
|
||||
r"(?: · chunk \d+/\d+)?$"
|
||||
),
|
||||
timeout=SETTLE_TIMEOUT_MS,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user