phase: 183_sync_passive_delete_cascade
All gates green. Final state verified.
**Implemented / verified**
- Task 03's work was **never actually done** (its report was a stub, `tests/e2e/test_prune_cascade_cleanup.py` did not exist) → wrote it: 3 tests — 9-file local source synced from the Sources page (`9 added`), 6 files deleted upstream, re-sync (`0 added · 3 unchanged · 6 pruned`), documents vanished from the tree + `GET /api/documents/content` 404 + total 3, **0 orphan chunk rows** via the test's own SQL, survivor chunk total unchanged; partial-prune chat turn (grounded, non-deflected, `query_log` names the survivor and no pruned doc); total source removal (`DELETE /api/git-sources/{id}`) wipes every doc+chunk of the source with 0 orphans, sibling source untouched. Mock-only (`E2E_REAL_LLM=1` skipped), own port 8199, truncate before/after.
- Tasks 01/02 re-verified: `passive_deletes=True` + mapper pin, probe flipped to `EXPECTED_CHUNK_SELECTS_PER_PRUNED_DOC = 0`, `_prune`/`delete_git_source` docstrings.
- **Drift guard proven non-tautological by my own one-off run**: scratch DB (`alembic upgrade head`) → FK `confdeltype='c'`, guard PASSES, CASCADE delete leaves 0 chunks; FK recreated `ON DELETE NO ACTION` → guard FAILS verbatim and a `passive_deletes` delete raises `ForeignKeyViolation` (never silent orphans). Scratch DB dropped; shared test DB untouched (`'c'`).
**Gates**
- `uv run pytest --cov=app --cov-report=term-missing` → **4488 passed**, `app/` TOTAL **99%** (>90%)
- `uv run pytest tests/integration/test_prune_cascade_statements.py -v` → 4 passed
- `uv run pytest tests/e2e/test_prune_cascade_cleanup.py -v --no-cov` → **3 passed in isolation**; `test_source_removal_cleanup.py` 6 passed, `test_force_push_resync.py` 2 passed (both isolated)
- `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors
**Completion criteria**: passive_deletes+pin ✅ · probe post-fix ✅ · drift guard green + proven-failing variant ✅ · full suite/coverage/lint ✅ · E2E in isolation ✅ · docs consistent ✅ · commit = harness-only (per pipeline rule I ran no git write commands).
**Docs**: fixed 4 pre-existing README inconsistencies — per-module E2E ports (claim of "one pair of fixed ports" predates phase 79), debugpy `launch.json` snippet vs the shipped file (`name`/`remoteRoot`), and the page-naming wobble (navbar labels `/sources.html` **RAG**, `/git-sources.html` **Sources** — the Sync button's home is now named correctly in 4 places); added the phase-183 E2E pointer. AGENTS.md verified accurate (rule 12 matches the delete-site audit).
**Deviation**: none from the locked decisions; the E2E is a behaviour pin (byte-identical owner-visible behaviour), the statement-count probe remains the memory teeth, as phase 183 D1/A4 specify.
**Next pending phase**: `184_sync_bounded_session`.
This commit is contained in:
@@ -1469,6 +1469,30 @@ def test_prune_records_changed_paths_only_when_summary_given(db, tmp_path: Path)
|
||||
_cleanup_source(db, root.name)
|
||||
|
||||
|
||||
# ---------- phase 183 (task 02): the prune deletes IN the database ----------
|
||||
|
||||
|
||||
def test_document_chunks_relationship_is_passive_deletes() -> None:
|
||||
"""``Document.chunks`` is ``passive_deletes=True`` (phase 183, LOCKED A1).
|
||||
|
||||
The mapper-level pin is the cheapest place to catch the keyword being
|
||||
dropped: without it ``session.delete(doc)`` SELECTs every chunk of the
|
||||
document before deleting it, hydrating every 768-dim
|
||||
``Chunk.embedding`` (~24.8 KiB of Python each) — the shape that made a
|
||||
3,008-document prune cost ~720 MB and locked up the 2026-10-15 VM
|
||||
(3,904 MB, no swap). The other half of the contract — the FK really is
|
||||
``ON DELETE CASCADE`` — is pinned live by
|
||||
``tests/integration/test_prune_cascade_statements.py``
|
||||
(``test_chunks_fk_cascades_at_database_level``), so these two tests
|
||||
fail together the moment either side drifts.
|
||||
"""
|
||||
rel = Document.__mapper__.relationships["chunks"]
|
||||
assert rel.passive_deletes is True
|
||||
# The ORPHAN semantics ``_replace_content_chunks``' re-embed relies on
|
||||
# (``doc.chunks = [...]`` drops the old rows) stay intact (A2).
|
||||
assert "delete-orphan" in str(rel.cascade)
|
||||
|
||||
|
||||
# ---------- phase 129 (task 02): on_chunk_progress plumbing ----------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user