phase: 167_api_payload_diet
All gates green. Final report: **Verified (phase 167 final pass — all 6 tasks already in `complete/`)** - Live re-measure of the headline criterion on the **1,000-doc live-replica corpus** (restored `data/bor_live_kb_replica.dump.sql` into a scratch DB `bor_audit`, alembic 0011→0027, dropped after; the shared dev DB was never touched): `GET /api/docs/tree` default depth=1 = **8,474 identity → 1,128 B with `Content-Encoding: gzip`** (< 40 KB bar; ~2 MB defect fixed), repeat load **304 / 0 bytes**; depth=8 265,147→23,515; node drill 20,844→1,065; `/api/docs` 246,366→45,104; `/api/documents/content` 892,124→60,776 (`LOOK`, accepted-with-why). Reproduces `AUDIT.md` to within 1 byte. - Transport contracts proven live: `POST /api/chat` **no** `Content-Encoding` + frames arriving live + budget line `… enc=identity duration_ms=7380 stream=1`; HTML still `no-cache` with the `?v=` rewrite (and gzipped); A20 headers on 200 **and** 304; ETag identical across identity/gzip; `depth=0|9|abc`→422; anonymous tree/node→403; unknown folder/source→404 `unknown node`. **Gates (all 100% green)** - `uv run pytest` → **3980 passed**; `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99%** (`compression.py`, `http_cache.py`, `response_budget.py` 100%; `api/docs.py` 99%, one defensive dedupe line). - `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors. - Phase + regression E2E **in isolation** (`--junitxml`, exit codes): `test_kb_tree_lazy_load` 11, `test_kb_tree` 8, `test_kb_tree_nav` 4, `test_navbar_refresh` 7, `test_document_dates` 6, `test_git_source_dates` 4, `test_sync_summary_visibility` 3, `test_table_ux_polish` 6, `test_sources_midstream_bug` 6, `test_ls_tree_drilldown` 3, `test_smoke` 3 — **0 failures/errors** (the earlier `-q -q` run had simply suppressed pytest's summary line). **Documentation** — README.md + AGENTS.md reviewed in full (1,436 / 80 lines): - Fixed (pre-existing, phase-167-exposed): README *Caching & deploys* claimed "One Starlette middleware (`app/core/caching.py`) applies the rule" while the section now describes three transport layers → rewritten to name caching/compression/http_cache, the real outermost→innermost order, and why `bytes=` is uncompressed. - AGENTS.md rule 5 (UI Structure Check) gained the phase-167 clause: a lazily-drilled level keeps the current level rendered, its in-flight `role="status"` / `role="alert"`+Retry row lives in the same bounded scroll panel, and stat cards read server `totals` (never a walk of a depth-limited tree). - Cross-checked and consistent: every `BOR_*` in `.env.example` is in the README table, every `Settings` field is documented, all 15 README-cited API routes exist in the app, no stale "one fetch / drill client-side" claim remains anywhere. **Notable decisions / deviations** - `compression.py` adds a documented `_RebufferSmallResponses` seam beyond the D1 sketch (the caching layer's `BaseHTTPMiddleware` re-framing otherwise defeats Starlette's `minimum_size` exemption and inflates tiny responses) — integration-pinned, math still Starlette's. - A node-route **404 = vanished folder → honest reset to top level** (per `00_phase.md` D4/D6), not task-06's "alert + Retry"; 500 shows alert+Retry with the level preserved — both pinned, nothing weakened. - Retrieval battery not measurable (shared E2E DB holds no corpus), but `git diff --name-only -- app/rag` is empty, so retrieval is unchanged by construction. `AUDIT.md` left byte-identical (it lives under `todo/`, off-limits to this pass); no git index/history commands run. **Next pending phase:** none — `167_api_payload_diet` is the only entry in `.agents/phases/todo/`; the harness moves it to `complete/` and commits.
This commit is contained in:
@@ -154,7 +154,10 @@ def test_tree_file_dates_and_derived_subtree_max(admin_client, db) -> None:
|
||||
_seed_doc(db, "Homelab", "a/top.md", "Top", 1, base, C2)
|
||||
_seed_doc(db, "Homelab", "root.md", "Root", 0, base, C0)
|
||||
try:
|
||||
r = admin_client.get("/api/docs/tree")
|
||||
# Phase 167 (task 02): this D9 walk visits ``a`` → ``a/b`` →
|
||||
# ``a/b/c``, so it asks for the WHOLE tree (depth=8); the
|
||||
# endpoint's default (depth=1) is the RAG view's first paint.
|
||||
r = admin_client.get("/api/docs/tree?depth=8")
|
||||
assert r.status_code == 200
|
||||
sources = r.json()["sources"]
|
||||
# Registry order leads; the 0-document registered source lists.
|
||||
@@ -198,6 +201,49 @@ def test_tree_file_dates_and_derived_subtree_max(admin_client, db) -> None:
|
||||
_truncate(db)
|
||||
|
||||
|
||||
def test_tree_truncated_folder_keeps_the_recursive_date_at_default_depth(
|
||||
admin_client, db
|
||||
) -> None:
|
||||
"""Phase 167 (task 02, D2/A2) — the truncation-proof counterpart of
|
||||
the D9 walk above: at the DEFAULT depth (the view's first paint)
|
||||
the deep levels are not sent, but the truncated folder node still
|
||||
carries the phase-106 D9 contract — ``updated_at`` = the subtree's
|
||||
recursive MAX (deep C3) and ``documents`` = the recursive count —
|
||||
alongside ``truncated: true`` and ``children: []`` (the builder's
|
||||
max-date map is what buys this: the payload diet never loses the
|
||||
date column). The server ``totals`` ride the same response.
|
||||
"""
|
||||
_truncate(db)
|
||||
base = datetime.now(UTC)
|
||||
db.add(GitSource(url="https://github.com/reese/Homelab.git", kind="git", added_at=base))
|
||||
_seed_doc(db, "Homelab", "a/b/c/deep.md", "Deep", 1, base, C3)
|
||||
_seed_doc(db, "Homelab", "a/b/shallow.md", "Shallow", 0, base, C1)
|
||||
_seed_doc(db, "Homelab", "a/top.md", "Top", 1, base, C2)
|
||||
_seed_doc(db, "Homelab", "root.md", "Root", 0, base, C0)
|
||||
try:
|
||||
r = admin_client.get("/api/docs/tree") # the DEFAULT depth = 1
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
homelab = _tree_node(body["sources"], "Homelab")
|
||||
# One level only: the folder ``a`` is present but NOT built out.
|
||||
(a,) = [c for c in homelab["children"] if c["kind"] == "folder"]
|
||||
assert (a["path"], a["truncated"], a["children"]) == ("a", True, [])
|
||||
# The truncation-proof fields: recursive count + recursive date.
|
||||
assert a["documents"] == 3
|
||||
assert a["updated_at"] == C3.isoformat() # the DEEP max, unbuilt children
|
||||
# The source node's own max still spans the whole (unsent) subtree.
|
||||
assert homelab["updated_at"] == C3.isoformat()
|
||||
assert "truncated" not in homelab # depth >= 1: never truncated, key omitted
|
||||
# Server totals beside the depth-limited tree (the stat cards).
|
||||
assert body["totals"] == {
|
||||
"documents": 4,
|
||||
"chunks": 2,
|
||||
"last_indexed": base.isoformat(),
|
||||
}
|
||||
finally:
|
||||
_truncate(db)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PATCH /api/documents/date (D7) — the admin document-date editor.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user