phase: 194_sync_pending_count
Build and Push Containers / build-and-push-app (push) Successful in 2m5s
Build and Push Containers / build-and-push-db (push) Successful in 13s

**Phase 194 — final verification pass: all green.**

**Verified (all 5 tasks already complete; nothing re-implemented needed)**
- `app/rag/importer.py`: `_prescan_pending` + `_pending_hash_stmt` (scalar `documents.file_hash`, own session, ONE `asyncio.to_thread` span, REPLACES the stats pre-walk), `on_scan_progress`, `progress` pair = pending position, `0` branch = byte-identical phase-193 walk.
- `app/config.py`/`settings_catalog.py`/`.env.example`: `sync_pending_count=1` + validator; `app/api/sync.py`: `"scan"` phase, `scan_done`/`scan_total` (additive after `files_total`, kept at every terminal), forced/throttled RSS split; `frontend/assets/sources.js`: `fmtSyncPhaseLabel` scan branch first-arm, `fmtSyncLabel` untouched.

**Tests / lint / coverage (exact commands)**
- `uv run pytest --cov=app --cov-report=term-missing` → **4835 passed**, `app/` 99% (7240 stmts / 16 miss), exit 0
- `uv run pytest tests/e2e/test_sync_pending_count.py -v --no-cov` → **8 passed in 150.81s** (phase suite, isolated)
- 14 sync-lineage E2E suites, one pytest process each (`test_sync_remaining_count`, `…_chunk_progress`, `…_progress_box`, `…_peak_memory_visible`, `…_label_mobile`, `…_upload_progress`, `test_archive_upload_sources`, `…_image_progress`, `…_summary_visibility`, `…_status_freshness`, `test_smoke`, `test_fast_path_deferred_content`, `test_sync_session_reset`, `test_sync_git_freeze`) → **all exit 0**
- `uv run ruff check .` → All checks passed; `uv run pyright` → **0 errors**

**Completion criteria** — all met: `N remaining` = predicted change set (E2E arms 1/2/5, first import counts the walk, no-change → no count part); `scan` phase visible + flips to `import`, scan pair reset/kept at terminals (`test_sync_scan_phase.py`); `BOR_SYNC_PENDING_COUNT=0` walk semantics ("7 remaining" arm) + **negative refuses startup naming the field (verified live by importing `app.main`)**; pre-scan off-loop + cancel lands `cancelled` (integration pins); CLI/upload byte-identical (unedited pins green); payload additive (`_PRE_187_STATUS_KEYS | _MEMORY_KEYS | _SCAN_KEYS`); suite green, coverage >90%, lint/types clean.

**Documentation** — 3 fixes: README sync "States" bullet had an **unclosed parenthesis** (pre-existing, 193/194) and the scan-label "total is 0" clause was ambiguous about a `=0` run (it still counts the walk once the import starts); **AGENTS.md rule 17 had a stray `)`** (pre-existing from phase 191). Also verified consistency both directions for all 87 settings ↔ README ↔ `.env.example` ↔ settings catalog, the pages table ↔ `app/main.py` routes, no stale `BOR_` names, and README/AGENTS claims through phase 194.

**Notes/deviations** — no live-infra changes (prod sync knob ships default-on, no deployment touched); no git index/history commands run (all changes left in the working tree for the harness); commit criterion left to the harness as instructed.

**Next pending phase:** none — `.agents/phases/todo/` holds only `194_sync_pending_count`, which is now ready to move to `complete/`.
This commit is contained in:
2026-10-20 06:36:21 -04:00
parent ae080ac7f3
commit a45f744604
55 changed files with 7610 additions and 179 deletions
+11 -3
View File
@@ -231,12 +231,19 @@ PRE_187_STATUS_KEYS = frozenset(
)
MEMORY_KEYS = frozenset({"rss_mb", "rss_peak_mb"})
#: Phase 194 (task 02): the two additive PRE-SCAN position keys — the next
#: link of the same additive chain (the payload is exactly the pre-187 set
#: plus the memory pair plus the scan pair).
SCAN_KEYS = frozenset({"scan_done", "scan_total"})
#: The pre-phase integer count slots (their types are part of the
#: unchanged-shape pin in test 2 — never a float, never null).
_COUNT_KEYS = frozenset(
{
"files_done",
"files_total",
"scan_done",
"scan_total",
"current_chunk",
"chunks_total",
"images_done",
@@ -643,8 +650,9 @@ def test_status_payload_reports_rss(page: Page, app_url: str, db_ready: None) ->
running = _wait_sync_state(page, app_url, {"running"})
assert running["state"] == "running", running
# The additive contract on the wire: exactly the pre-phase keys plus
# the memory pair — nothing renamed, nothing dropped.
assert set(running) == PRE_187_STATUS_KEYS | MEMORY_KEYS, set(running)
# the memory pair plus the phase-194 scan pair — nothing renamed,
# nothing dropped.
assert set(running) == PRE_187_STATUS_KEYS | MEMORY_KEYS | SCAN_KEYS, set(running)
for key, value in running.items():
if key in MEMORY_KEYS:
# The documented type: int | null (null is "no reading").
@@ -674,7 +682,7 @@ def test_status_payload_reports_rss(page: Page, app_url: str, db_ready: None) ->
# --- after the terminal ---------------------------------------------
terminal = _wait_sync_state(page, app_url, {"success", "failed", "cancelled"})
assert terminal["state"] == "success", terminal
assert set(terminal) == PRE_187_STATUS_KEYS | MEMORY_KEYS, set(terminal)
assert set(terminal) == PRE_187_STATUS_KEYS | MEMORY_KEYS | SCAN_KEYS, set(terminal)
current, peak = terminal["rss_mb"], terminal["rss_peak_mb"]
if MEMORY_KNOWABLE:
assert peak is not None and isinstance(peak, int), terminal