phase: 187_sync_peak_memory_line
**Phase 187 — final verification pass: all criteria met, repo 100% green.**
Verified (all four tasks already in `complete/`)
- `app/core/memory.py`: `rss_mb` / `rss_peak_mb` / `cgroup_peak_mb` / `fmt_mb`, pure stdlib, never-raise, **100%** coverage, 24 branch tests.
- Lifecycle lines append-only: `sync: started … rss_mb=`, all three terminals `… rss_mb= rss_peak_mb=` (failed = sanitized one-liner beside the surviving `logger.exception`), `import: summary … rss_mb=`; prefixes byte-identical.
- `GET /api/sync/status`: additive `rss_mb`/`rss_peak_mb` (`int|null`), keyed off the ONE `_sample_rss` (run start, phase transitions, forced terminals, 5 s-throttled hook), `peak >= current` invariant, anonymous 403.
- `sources.js` `fmtSyncPeakClause`: ` · peak <N> MB` trailing the counts line / "Sync cancelled", nothing when null, no new control/live region.
Gates (exact commands)
- `uv run pytest --cov=app --cov-report=term-missing` → **4570 passed**, TOTAL **99%**, `app/api/sync.py` **100%**, `app/core/memory.py` **100%**
- `uv run ruff check .` → clean · `uv run pyright` → **0 errors**
- `uv run pytest tests/e2e/test_sync_peak_memory_visible.py -v --no-cov` → **5 passed in isolation** (ran twice)
- All 27 touched E2E families re-run **in isolation**, all green (progress_box 8, cancel 3, status_freshness 4, summary_visibility 3, sync_button 3, session_reset 4, git_freeze 2, git_log_stream_dates 5, PDF/image suites, …)
Completion criteria: every checkbox ✅ except the commit + phase-dir move, which belong to the harness.
Documentation
- README reviewed in full (pages, commands, config tables cross-checked against `app/config.py` — 85 env rows, all defaults match; phase-187 ops section, greps, process-vs-cgroup caveat): consistent, no changes needed.
- `AGENTS.md`: **fixed one pre-existing inconsistency** — rule 6 claimed the no-CDN test covers "the index page"; it actually parametrizes 10 pages (all but `/doc-edit.html`).
Notable
- Two failures early in the pass were self-inflicted by running the E2E module concurrently with the full suite on the shared Postgres (TRUNCATE deadlock + one order-dependent `test_overview` flake); both vanish under the documented serial isolation — no code defect, no change made.
- **PLAN §9 still needs the owner's reconciliation** (phase 187 adds the sync memory fields + two status keys; PLAN is never agent-edited — AGENTS rule 10 carries the spec). `TODO.md` holds an unrelated owner note, left untouched.
Next pending phase: `188_chunks_hnsw_index`.
This commit is contained in:
@@ -71,14 +71,23 @@
|
||||
settle it), the Copy button's clipboard content equal to the server
|
||||
string exactly, and the shared page's panel Copy button + plain
|
||||
`/document.html` link the ONE owner-directed exception to that page's
|
||||
zero-controls rule) — phase 182 task 07) and
|
||||
meets WCAG 2.1 AA basics (semantic
|
||||
zero-controls rule) — phase 182 task 07), and the sync's peak-memory
|
||||
clause is TRAILING TEXT on the result line that already exists
|
||||
(` · peak <N> MB` on the RAG page's `#sync-progress` counts line
|
||||
or "Sync cancelled", written by the ONE `fmtSyncPeakClause` off
|
||||
`rss_peak_mb ?? rss_mb` — no new control, no new live region, no new
|
||||
element, no clause in a running label or in the failed terminal's
|
||||
empty box, and NOTHING rendered when the pair is null — phase 187
|
||||
task 03) and meets WCAG 2.1 AA basics (semantic
|
||||
landmarks, labels, contrast ≥4.5:1, focus-visible, aria-live for the
|
||||
stream).
|
||||
6. **"No CDN Rule":** all CSS, JS, fonts, and images must be served statically
|
||||
from within the FastAPI application. No `<script src="https://…">` or
|
||||
`<link href="https://…">` tags in HTML templates unless bundled locally.
|
||||
An integration test enforces this on the index page.
|
||||
An integration test enforces this on every served page except
|
||||
`/doc-edit.html` (the parametrized no-CDN check in
|
||||
`tests/integration/test_api.py` — the shell, the viewer, the login and
|
||||
the shared page).
|
||||
7. **"Debugpy Check":** any new entrypoint must keep the conditional import —
|
||||
`debugpy` is imported **only** when `DEBUGPY=1` (see
|
||||
`app/core/debugging.py`); default off, never imported otherwise.
|
||||
@@ -102,7 +111,35 @@
|
||||
`api method=… path=… status=… bytes=… enc=… duration_ms=…` line per
|
||||
`/api/*` response, INFO at/over `BOR_LOG_LARGE_RESPONSE_BYTES`, read it
|
||||
with `grep ' :: api '`, audit it with
|
||||
`uv run python -m scripts.api_payload_audit`, phase 167); UI-side,
|
||||
`uv run python -m scripts.api_payload_audit`, phase 167) **and** the
|
||||
sync's memory fields (phase 187 — `app/core/memory.py` reads PROCESS
|
||||
RSS, never the host's cgroup figure, and never raises: a `None`
|
||||
renders `?` through the ONE `fmt_mb` helper. `rss_mb=` is appended to
|
||||
`sync: started …` and to the importer's `import: summary …`, and
|
||||
`rss_mb=` + `rss_peak_mb=` to all three terminals (`sync: done …`,
|
||||
`sync: cancelled by user …`, and the sanitized one-line
|
||||
`sync: failed state=failed … error=…` that sits beside — never in
|
||||
place of — the `logger.exception` traceback). Append-only: no
|
||||
pre-existing field renamed, reordered or re-typed, so every older
|
||||
grep still matches; read them with
|
||||
`grep ' :: ' server.log | grep ' rss_peak_mb='`. Phase 187 task 03
|
||||
puts the SAME pair on the status: `SyncStatus.rss_mb` /
|
||||
`rss_peak_mb`, written by the ONE `_sample_rss` — forced at the run
|
||||
start, at every `phase` transition and in every terminal (which keep
|
||||
the FINAL pair beside the final counts, the phase-64 convention, and
|
||||
log exactly what the payload reports) — and throttled to one read per
|
||||
`_RSS_SAMPLE_INTERVAL_S` (5 s) from the per-file progress hook, so a
|
||||
27k-file walk costs ~600 `/proc` reads and not 27k (LOCKED A4).
|
||||
`GET /api/sync/status` reports both ADDITIVELY as `rss_mb` /
|
||||
`rss_peak_mb` (`int | null`; `null` is "no reading", never 0) and the
|
||||
failed funnel samples too, because the run that died is the run whose
|
||||
memory matters. That ONE sampler also owns the pair's invariant: the
|
||||
kernel refreshes `ru_maxrss` lazily, so a raw peak can sit BELOW the
|
||||
raw `VmRSS` (measured 164 vs 162) and `_sample_rss` lifts it to the
|
||||
current reading — every surface (both lines, the payload, the page's
|
||||
clause) therefore satisfies `peak >= current`, never a peak below the
|
||||
run's own footprint, and an unknowable peak is never fabricated from
|
||||
the current reading); UI-side,
|
||||
honor the "never stale" feedback contract (PLAN §7.4).
|
||||
11. **Never block the event loop:** anything blocking (a `subprocess`,
|
||||
synchronous file/network I/O, a heavy walk) reached from an `async`
|
||||
|
||||
Reference in New Issue
Block a user