phase: 149_sync_progress_box
Build and Push Containers / build-and-push-app (push) Successful in 3m35s
Build and Push Containers / build-and-push-db (push) Successful in 11s

All verification complete. The `ink-soft`/`--surface` pair the box uses is already pinned AA by the existing theme E2E suites (8.2:1 dark, ~6.9:1 light). Everything is green — no fixes were needed.

**Phase 149 final verification report**

- Verified (no re-work needed): `#sync-progress` box wiring (single `setSyncLabel` writer with `running` flag; `is-running` class travels with the spin; aria-label owned by the writer), the panel/icon-only-button CSS, and the 5-test dedicated E2E; all 6 pre-existing sync E2Es' running pins migrated to the box, settled pins untouched.
- Dedicated E2E: `uv run pytest tests/e2e/test_sync_progress_box.py -v --no-cov` → **5 passed in 198.6 s** (isolation, slow-proxy mock; under the 600 s budget).
- Full suite: `uv run pytest --cov=app --cov-report=term-missing` → **3463 passed, app/ coverage 99%** (>90% gate).
- Migrated sync E2Es, each in isolation: `test_sync_button` 3 passed, `test_sync_cancel` 3, `test_sync_chunk_progress` 2, `test_sync_label_mobile` 4, `test_sync_summary_visibility` 3, `test_sync_upload_progress` 4 — all green.
- Lint/types: `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors.
- Criteria: in-flight spinner-only button + full untruncated wrapped box (desktop & mobile 390×844, `scrollWidth ≤ clientWidth`) ✓; settle byte-identical ("Synced HH:MM" + `3 added`, box hidden, aria restored) ✓; `#sync-result` sole live region (box `aria-hidden`), mid-run reload re-attaches ✓; WCAG (AA contrast both themes via the pinned ink-soft/surface pair, focus-visible, reduced-motion kill intact at styles.css L2201) ✓; cancel/409/error/upload/status API unchanged (green suites above) ✓.
- Documentation: README.md reviewed in full (sync-states paragraph now matches the shipped UI; env-var table matches `app/config.py` and covers all `.env.example` vars; pages/commands verified) and AGENTS.md reviewed in full — **verified consistent, no changes needed**.
- Notable: no defects found in this or prior work; no live infrastructure touched.
- Next pending phase: none — `todo/` will be empty once the harness moves 149 (only 149 was pending).
This commit is contained in:
2026-10-01 21:14:48 -04:00
parent b233c8d714
commit dd524246ec
33 changed files with 2512 additions and 393 deletions
+15 -8
View File
@@ -11,7 +11,11 @@ The story gate runs the **real** sync path end to end — a real
network), a real import against the mock LLM, a real KB-overview
regeneration — plus the admin-only visibility and the full button
lifecycle (§7.4: "Syncing…" → "Synced HH:MM" + counts, the idempotent
second run, the 409 double trigger).
second run, the 409 double trigger). Phase 149 (task 01): the
RUNNING-state pins (the 202's "Syncing…") ride the detail box
(``#sync-progress`` — the running label left the pill; the button is
the spinner), while every SETTLED pin (the idle "Sync sources", the
"Synced HH:MM" label + counts line) is unchanged.
Per-module app env (the E2E conftest pattern, module-scoped): this
story's app boots with ``BOR_GIT_SOURCES=file://<fixture repo>`` and
@@ -246,11 +250,12 @@ def test_anonymous_sees_no_button(page: Page, app_url: str, db_ready: None) -> N
def test_admin_sync_lifecycle(page: Page, app_url: str, db_ready: None) -> None:
"""AC 2 + 4 — click → "Syncing…" (disabled) → "Synced HH:MM" + the
counts, against the REAL pipeline (git clone of the ``file://``
fixture → import with prune → KB overview regeneration, mock LLM).
Then the idempotent second run: fast-forward pull + sha256 hash
skip → "0 added · 1 unchanged"."""
"""AC 2 + 4 — click → "Syncing…" (disabled; phase 149: the
running label is in the detail box — the button is the spinner)
→ "Synced HH:MM" + the counts, against the REAL pipeline (git
clone of the ``file://`` fixture → import with prune → KB overview
regeneration, mock LLM). Then the idempotent second run:
fast-forward pull + sha256 hash skip → "0 added · 1 unchanged"."""
login(page, app_url) # lands on /sources.html (the button's home)
btn = page.locator("#sync-btn")
@@ -260,7 +265,9 @@ def test_admin_sync_lifecycle(page: Page, app_url: str, db_ready: None) -> None:
# --- run 1: clone + import + overview --------------------------------
btn.click()
expect(btn).to_be_disabled()
expect(page.locator("#sync-label")).to_have_text("Syncing…")
# Phase 149 (task 01): the running detail rides the box under the
# button row (the button is the spinner) — the 202's bare label.
expect(page.locator("#sync-progress")).to_have_text("Syncing…")
expect(page.locator("#sync-label")).to_have_text(SYNCED_LABEL, timeout=SYNC_TIMEOUT_MS)
expect(btn).to_be_enabled() # never stale — re-enabled at the terminal state
# The fixture doc (one A9-format file) is the only change.
@@ -282,7 +289,7 @@ def test_admin_sync_lifecycle(page: Page, app_url: str, db_ready: None) -> None:
# --- run 2: idempotent pull + hash skip -------------------------------
btn.click()
expect(btn).to_be_disabled()
expect(page.locator("#sync-label")).to_have_text("Syncing…")
expect(page.locator("#sync-progress")).to_have_text("Syncing…")
expect(page.locator("#sync-label")).to_have_text(SYNCED_LABEL, timeout=SYNC_TIMEOUT_MS)
expect(btn).to_be_enabled()
# Nothing re-embedded (sha256 delta) — the no-op run announces the