phase: 118_summary_seed_context
**Phase 118 final verification pass — complete.** All criteria verified; 4 pre-existing defects found and fixed.
- **Verified:** summary-seed wiring (`select_suggested` top-5 no-floor → summary blocks, no full text in HIGH prompt), all-doc markdown summaries + NULL backfill (`summary_backfilled`, no `sources_meta` bump), `read` adds full text with `read_docs`-only dedupe, `done.sources` = suggested+read / durable record = suggested+related+read + `suggested=N` log line (seen live in E2E), byte-locked PERSONA/LOW/TOOLS_SECTION, battery gate PASS recorded in `TOOL_CALLING_TESTING.md` §10 (turbo 2026-09-16: 1/2/4 GREEN, cond-3 reported 9/10 per A7, contract 21/21, caps 0).
- **Defects fixed (all pre-existing, none phase-118):** ① `ChatMessage` schema missing the phase-113 `related` key → `extra="forbid"` 422'd every done-time auto-save of grounded turns with a related tier, leaving `message_count=1` (root cause of `test_share_chat` 3F; browser-level instrumentation proved the PUT 422) — added the field + unit/integration pins; ② `test_theme_semantic_completion` pins stale vs phase-117 debox (border/chip removed) — re-targeted to assert border/chip *absence*; ③ `test_header_consistency` `<26`px pin red on 26.125px native date-input line — bound relaxed to `<34` (wrap-detection intent kept); ④ `test_navbar_refresh` bor.chat.v1 key set updated for `related`.
- **Test/lint/coverage:** `uv run pytest --cov=app --cov-report=term-missing` → **2506 passed, app/ 99%** (>90%); `uv run ruff check . && uv run pyright` → clean, 0 errors.
- **E2E:** new story suite in isolation → **2 passed**; full 103-suite matrix sweep (each isolated) → **all 103 green** after the fixes; `test_share_chat` 4 passed, `test_theme_semantic_completion` 8 passed, `test_header_consistency` 3 passed, `test_navbar_refresh` 7 passed.
- **Deviations:** none from LOCKED decisions. Note: orphaned diagnostic uvicorn processes briefly made E2E sessions exercise stale code — killed and re-verified; a sweep-regenerated tracked screenshot was restored. No commits made (harness commits).
- **Completion criteria:** all 7 ✅ (commit/phase-move is the harness's step).
- **Next pending phase:** none — `todo/` holds only this phase's overview pending the harness move.
This commit is contained in:
@@ -11,12 +11,16 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from app.config import Settings
|
||||
from app.models import Document
|
||||
from app.rag import retriever
|
||||
from app.rag.retriever import (
|
||||
TRUNCATION_MARKER,
|
||||
RetrievedChunk,
|
||||
select_documents,
|
||||
select_documents_tiered,
|
||||
select_related,
|
||||
select_suggested,
|
||||
)
|
||||
|
||||
|
||||
@@ -258,6 +262,231 @@ def test_select_documents_wrapper_is_legacy_tiering() -> None:
|
||||
)[0]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Phase 118 — select_suggested: the top-N "start here" tier, NO floor (A3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_suggested_rank_order_by_first_seen_chunk() -> None:
|
||||
"""The SAME stable walk as ``select_documents_tiered``: a document's
|
||||
rank is fixed by its FIRST seen chunk in score-descending order — a
|
||||
doc whose best chunk appears later in the input list still ranks
|
||||
where that chunk falls."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
c = _doc("c.md", "C" * 50)
|
||||
chunks = [
|
||||
_chunk(a, 0.4, position=0), # a's weak chunk comes first
|
||||
_chunk(b, 0.8),
|
||||
_chunk(a, 0.9, position=2), # a's best chunk comes last
|
||||
_chunk(c, 0.5),
|
||||
]
|
||||
out = select_suggested(chunks, n=5)
|
||||
assert [d.path for d in out] == ["a.md", "b.md", "c.md"]
|
||||
# The rows carry the full content byte-identical (A6: the content is
|
||||
# what ``read`` serves later — never truncated).
|
||||
assert out[0].content == "A" * 50
|
||||
assert TRUNCATION_MARKER not in out[0].content
|
||||
|
||||
|
||||
def test_suggested_dedupes_multiple_chunks_to_one_row() -> None:
|
||||
"""Multiple hit chunks of one document collapse to a single row."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
chunks = [
|
||||
_chunk(a, 0.2),
|
||||
_chunk(b, 0.7),
|
||||
_chunk(a, 0.9, position=2),
|
||||
_chunk(a, 0.5),
|
||||
]
|
||||
out = select_suggested(chunks, n=5)
|
||||
assert [d.path for d in out] == ["a.md", "b.md"] # one row per document
|
||||
assert out[0] is a
|
||||
|
||||
|
||||
def test_suggested_caps_at_n_in_rank_order() -> None:
|
||||
docs_in = [_doc(f"d{i}.md", "X" * 20) for i in range(7)]
|
||||
chunks = [_chunk(d, 0.9 - 0.1 * i) for i, d in enumerate(docs_in)]
|
||||
out = select_suggested(chunks, n=3)
|
||||
assert [d.path for d in out] == ["d0.md", "d1.md", "d2.md"]
|
||||
|
||||
|
||||
def test_suggested_default_cap_is_the_settings_value(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""``n`` omitted → ``BOR_SUGGESTED_DOCS`` caps the walk — default 5
|
||||
(LOCKED A3, the top-5 "start here" directive, TODO L3) — and the cap
|
||||
is the setting's LIVE value, not a frozen constant."""
|
||||
docs_in = [_doc(f"d{i}.md", "X" * 20) for i in range(7)]
|
||||
chunks = [_chunk(d, 0.9 - 0.1 * i) for i, d in enumerate(docs_in)]
|
||||
settings = Settings(_env_file=None) # pyright: ignore[reportCallIssue]
|
||||
assert settings.suggested_docs == 5 # the production default
|
||||
monkeypatch.setattr(retriever, "get_settings", lambda: settings)
|
||||
assert [d.path for d in select_suggested(chunks)] == [
|
||||
f"d{i}.md" for i in range(5)
|
||||
]
|
||||
small = Settings(_env_file=None, suggested_docs=2) # pyright: ignore[reportCallIssue]
|
||||
monkeypatch.setattr(retriever, "get_settings", lambda: small)
|
||||
assert [d.path for d in select_suggested(chunks)] == ["d0.md", "d1.md"]
|
||||
|
||||
|
||||
def test_suggested_never_filters_on_cosine_floor() -> None:
|
||||
"""NO floor (LOCKED A3): a lexical-only hit (cosine 0.0 by
|
||||
construction) is a suggestion when it ranks — the contrast pin
|
||||
against ``select_documents_tiered``'s floored cited tier on the SAME
|
||||
input, which demotes it to the related tier."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
lexical_only = RetrievedChunk(
|
||||
chunk_id=uuid.uuid4(),
|
||||
position=0,
|
||||
content="X" * 10,
|
||||
score=0.9, # top fused rank (the FTS hit)
|
||||
document=a,
|
||||
cosine=0.0, # no vector rank — lexical-only
|
||||
fts_hit=True,
|
||||
)
|
||||
chunks = [lexical_only, _cos_chunk(b, 0.8, 0.5)]
|
||||
# Suggested: the floor never filters — a leads, b follows in rank order.
|
||||
assert [d.path for d in select_suggested(chunks, n=5)] == ["a.md", "b.md"]
|
||||
# Contrast: the same input through the phase-113 cited tier — the
|
||||
# 0.35 usefulness bar demotes the lexical-only doc to related.
|
||||
cited, related = select_documents_tiered(chunks, n=2, floor=0.35, related_cap=2)
|
||||
assert [d.path for d in cited] == ["b.md"]
|
||||
assert [d.path for d in related] == ["a.md"]
|
||||
|
||||
|
||||
def test_suggested_tie_break_inherited_from_fused_order() -> None:
|
||||
"""Equal fused scores keep the input (fused) order — the stable
|
||||
score-only walk inherits ``fuse()``'s (-score, -cosine, path,
|
||||
position) tie-break; the selector never re-sorts it away."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
chunks = [_cos_chunk(a, 0.7, 0.5), _cos_chunk(b, 0.7, 0.4)] # a wins on cosine
|
||||
assert [d.path for d in select_suggested(chunks, n=5)] == ["a.md", "b.md"]
|
||||
# A FULL tie (score AND cosine): the input position — ``fuse()``'s
|
||||
# path/position tie-break already applied — decides. ``m.md`` sorts
|
||||
# AFTER ``b2.md`` alphabetically, so any re-sort by path would flip
|
||||
# the order; the fused input order must win.
|
||||
m = _doc("m.md", "M" * 50)
|
||||
b2 = _doc("b2.md", "B" * 50)
|
||||
chunks = [_cos_chunk(m, 0.7, 0.4), _cos_chunk(b2, 0.7, 0.4)]
|
||||
assert [d.path for d in select_suggested(chunks, n=5)] == ["m.md", "b2.md"]
|
||||
|
||||
|
||||
def test_suggested_empty_chunks_yield_no_documents() -> None:
|
||||
assert select_suggested([], n=5) == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Phase 118, task 05 — select_related: the rank-6+ tier after the suggested set
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_related_walk_order_after_excluded_set() -> None:
|
||||
"""The SAME stable score-descending walk as ``select_suggested``:
|
||||
a document's rank is fixed by its FIRST seen chunk; documents in
|
||||
*excluded_ids* (the suggested set) are skipped and the rest come
|
||||
back in rank order."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
c = _doc("c.md", "C" * 50)
|
||||
d = _doc("d.md", "D" * 50)
|
||||
chunks = [
|
||||
_chunk(a, 0.4, position=0), # a's weak chunk comes first
|
||||
_chunk(b, 0.8),
|
||||
_chunk(a, 0.9, position=2), # a's best chunk last — a ranks first
|
||||
_chunk(c, 0.5),
|
||||
_chunk(d, 0.3),
|
||||
]
|
||||
out = select_related(chunks, {a.id, b.id}, cap=2)
|
||||
assert [x.path for x in out] == ["c.md", "d.md"]
|
||||
|
||||
|
||||
def test_related_skips_excluded_documents() -> None:
|
||||
"""Every document in *excluded_ids* is skipped, even when it would
|
||||
rank inside the cap — the suggested set never rides the related row."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
c = _doc("c.md", "C" * 50)
|
||||
chunks = [_chunk(a, 0.9), _chunk(b, 0.8), _chunk(c, 0.5)]
|
||||
out = select_related(chunks, {a.id, b.id}, cap=5)
|
||||
assert out == [c]
|
||||
# Every doc excluded → empty, even with room left in the cap.
|
||||
assert select_related(chunks, {a.id, b.id, c.id}, cap=5) == []
|
||||
|
||||
|
||||
def test_related_caps_at_cap_in_rank_order() -> None:
|
||||
"""The phase-118 turn wiring on 9 docs: suggested = the top 5,
|
||||
related = rank 6–7 (capped at 2), disjoint from the suggested set."""
|
||||
docs_in = [_doc(f"d{i}.md", "X" * 20) for i in range(9)]
|
||||
chunks = [_chunk(d, 0.9 - 0.1 * i) for i, d in enumerate(docs_in)]
|
||||
suggested = select_suggested(chunks, n=5)
|
||||
out = select_related(chunks, {d.id for d in suggested}, cap=2)
|
||||
assert [x.path for x in out] == ["d5.md", "d6.md"] # rank 6–7, capped
|
||||
suggested_paths = {d.path for d in suggested}
|
||||
assert suggested_paths.isdisjoint({x.path for x in out})
|
||||
|
||||
|
||||
def test_related_cap_zero_yields_empty() -> None:
|
||||
"""cap=0 is the kill switch (related_max_docs=0): no related docs,
|
||||
the pre-phase-113 visibility."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
c = _doc("c.md", "C" * 50)
|
||||
chunks = [_chunk(a, 0.9), _chunk(b, 0.8), _chunk(c, 0.5)]
|
||||
assert select_related(chunks, {a.id}, cap=0) == []
|
||||
|
||||
|
||||
def test_related_never_filters_on_cosine_floor() -> None:
|
||||
"""NO floor: a lexical-only (cosine 0.0) doc is related when it
|
||||
ranks after the excluded set — the related tier is visibility, not
|
||||
citation (phase 118 applies no cosine floor to it)."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
lexical_only = RetrievedChunk(
|
||||
chunk_id=uuid.uuid4(),
|
||||
position=0,
|
||||
content="X" * 10,
|
||||
score=0.9, # top fused rank (the FTS hit)
|
||||
document=a,
|
||||
cosine=0.0, # no vector rank — lexical-only
|
||||
fts_hit=True,
|
||||
)
|
||||
chunks = [lexical_only, _cos_chunk(b, 0.8, 0.5)]
|
||||
out = select_related(chunks, set(), cap=5)
|
||||
assert [x.path for x in out] == ["a.md", "b.md"]
|
||||
|
||||
|
||||
def test_related_dedupes_multiple_chunks_to_one_row() -> None:
|
||||
"""Multiple hit chunks of one document collapse to a single row
|
||||
(first-seen-chunk rank, dedupe by document.id — the shared walk)."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
chunks = [_chunk(a, 0.2), _chunk(b, 0.7), _chunk(a, 0.9, position=2)]
|
||||
out = select_related(chunks, set(), cap=5)
|
||||
assert [x.path for x in out] == ["a.md", "b.md"] # one row per document
|
||||
assert out[0] is a
|
||||
|
||||
|
||||
def test_related_tie_break_inherited_from_fused_order() -> None:
|
||||
"""Equal fused scores keep the input (fused) order — the stable
|
||||
score-only walk inherits ``fuse()``'s (-score, -cosine, path,
|
||||
position) tie-break; the selector never re-sorts it away."""
|
||||
a = _doc("a.md", "A" * 50)
|
||||
b = _doc("b.md", "B" * 50)
|
||||
chunks = [_cos_chunk(a, 0.7, 0.5), _cos_chunk(b, 0.7, 0.4)] # a wins on cosine
|
||||
assert [x.path for x in select_related(chunks, set(), cap=5)] == ["a.md", "b.md"]
|
||||
m = _doc("m.md", "M" * 50)
|
||||
b2 = _doc("b2.md", "B" * 50)
|
||||
chunks = [_cos_chunk(m, 0.7, 0.4), _cos_chunk(b2, 0.7, 0.4)] # full tie
|
||||
assert [x.path for x in select_related(chunks, set(), cap=5)] == ["m.md", "b2.md"]
|
||||
|
||||
|
||||
def test_related_empty_chunks_yield_no_documents() -> None:
|
||||
assert select_related([], set(), cap=5) == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hybrid retrieval (A7): RRF fusion + lexical tsquery
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user