All verification complete. Final report: **Phase 119 final verification pass — all criteria verified, one stale pin fixed.** - Verified implementation of all 6 tasks: D1 component name-hit rule (`name_hit` flag, titles never matched, retired length tie-break), D2 `BOR_NAME_HIT_BONUS` (0.005 default, 0 = byte-identical kill switch, negative fails startup, selection-layer only, `eval_retrieval` `suggested:` line), D3 suggested-folder lines (after `SUGGEST_INTRO`, before first block), D4 cite-discipline `SUGGEST_INTRO` sentence (PERSONA/LOW/`TOOLS_SECTION` byte-pins intact), D5 `done.sources` = read docs only (frontend no-op on empty confirmed), D6 mock `repeat your folder map` echo + new suite + telemetry. - Battery (replica restored per skill, fingerprint docs=1000/chunks=8866 verified, `eval_retrieval --from-file tests/fixtures/retrieval_battery.txt` re-run): **GATE PASS** — gitea README #4 in suggested top-5, forgejo 5/5 (README #1), gateway README in top-5 (#4), qwen3.8-27b quadlets top-5, Mongolia HIGH/fts=5 unchanged. - New E2E in isolation: `4 passed` ×2 (deterministic). All 27 modified E2E suites in isolation: 26 green; **1 stale pin fixed** — `test_source_chip_quality.py` durable-record order pin pre-dated the D1 re-rank (`aliases` stem sub-component name-hits `ssh_aliases.txt`, deterministically lifting `backups.md` over `kubernetes.md`; probe-verified 0.016277 vs 0.016036, 4/4 stable) — re-pinned with the phase-119 rationale; suite green ×2. - Gates: `uv run pytest --cov=app --cov-report=term-missing` → **2547 passed, app coverage 99%** (>90%); `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors. - Completion criteria: 1 ✅ (battery, recorded), 2 ✅ (folder lines; block/LOW byte-identical pins green), 3 ✅ (read-only chips, zero-read chips nothing, related row + durable record untouched — unit+E2E agree), 4 ✅ (all green), 5 → commit/phase-move left to the harness per pass rules (nothing committed). - Deviations: battery output + real-model telemetry recorded in `.agents/reports/119_name_signal_read_chips/task06_battery_and_e2e.md` and `TOOL_CALLING_TESTING.md` §11 (task files in `complete/` are immutable to this pass); gateway canonical doc at #4 vs overview's #3 was already documented at task 06 (containment gate met). - Next pending phase: **none** — `todo/` holds only phase 119.
247 lines
9.5 KiB
Python
247 lines
9.5 KiB
Python
"""Integration: the name-hit lexical signal against real Postgres (the
|
||
2026-09-05 "Qwen 3.8" incident).
|
||
|
||
The unit suite (``tests/unit/test_retriever.py``) covers the pure
|
||
mapping with fake rows; this suite covers the SQL side on real
|
||
Postgres: the document-projection scan, the LATERAL representative-
|
||
chunk fetch (the ``is_summary`` chunk wins, chunk 0 otherwise, and a
|
||
chunk-less name match is EXCLUDED — the ``c.id IS NOT NULL`` guard),
|
||
the (matched-token count, catalog) ranking (phase 119, LOCKED A2 —
|
||
the two-class component rule: digit-bearing prefix, digitless exact,
|
||
titles never matched), the name-hits-lead-the-lexical-list union with
|
||
the FTS rows (chunk-id dedup), and the full ``retrieve()`` →
|
||
``select_documents()`` path putting the versioned-name document into
|
||
the seeded top-N.
|
||
|
||
Requires: ``podman compose up -d db``.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import uuid
|
||
from collections.abc import Iterator
|
||
from datetime import UTC, datetime
|
||
|
||
import pytest
|
||
from sqlalchemy import text
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.models import Chunk, Document
|
||
from app.rag.retriever import (
|
||
NAME_HIT_LIMIT,
|
||
_lexical_candidates,
|
||
_name_hit_chunks,
|
||
retrieve,
|
||
select_documents,
|
||
)
|
||
|
||
INCIDENT_QUESTION = "What are the correct llama.cpp arguments for Qwen 3.8?"
|
||
|
||
#: 768-dim test vectors (the pgvector column's dimension) — axis unit
|
||
#: vectors so the cosines are exact (1.0 parallel, 0.0 orthogonal,
|
||
#: 0.7071 half-parallel).
|
||
D = 768
|
||
|
||
|
||
def _vec(axis: int, second: bool = False) -> list[float]:
|
||
v = [0.0] * D
|
||
v[axis] = 1.0
|
||
if second:
|
||
v[axis + 1] = 1.0
|
||
return v
|
||
|
||
|
||
def _doc(db: Session, source: str, path: str, title: str, content: str) -> Document:
|
||
doc = Document(
|
||
id=uuid.uuid4(),
|
||
source=source,
|
||
path=path,
|
||
full_path=f"/tmp/{source}/{path}",
|
||
title=title,
|
||
content=content,
|
||
content_hash="0" * 64,
|
||
indexed_at=datetime.now(UTC),
|
||
)
|
||
db.add(doc)
|
||
return doc
|
||
|
||
|
||
def _chunk(
|
||
db: Session, doc: Document, position: int, content: str, is_summary: bool = False
|
||
) -> Chunk:
|
||
chunk = Chunk(
|
||
id=uuid.uuid4(),
|
||
document_id=doc.id,
|
||
position=position,
|
||
content=content,
|
||
is_summary=is_summary,
|
||
)
|
||
db.add(chunk)
|
||
return chunk
|
||
|
||
|
||
@pytest.fixture()
|
||
def kb(db) -> Iterator[None]:
|
||
"""A fresh KB with the incident shape: the qwen3.8 quadlet (the
|
||
name hit, with a summary chunk + an ordinary chunk), a qwen3.6
|
||
quadlet (same family, different version — NOT a hit), an
|
||
unrelated document (FTS-only candidate), and a chunk-less document
|
||
whose name DOES carry the token (the exclusion guard)."""
|
||
db.execute(text("TRUNCATE chunks, documents"))
|
||
db.commit()
|
||
|
||
q38 = _doc(
|
||
db,
|
||
"deploy",
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.8-27b-juggernaut-vulkan.container",
|
||
"qwen3.8-27b-juggernaut-vulkan",
|
||
"# llama.cpp juggernaut\nExec=--port 8000 -ctk q8_0 -ctv q8_0 --jinja\n"
|
||
"-m /models/qwen3.8-27b/Qwen3.8-27B-UD-Q6_K.gguf\n",
|
||
)
|
||
_chunk(
|
||
db,
|
||
q38,
|
||
-1,
|
||
"Podman quadlet: llama.cpp server for Qwen 3.8 27B (juggernaut).",
|
||
is_summary=True,
|
||
)
|
||
_chunk(db, q38, 0, "# llama.cpp juggernaut\nExec=--port 8000 -ctk q8_0")
|
||
db.flush()
|
||
# A vector the question vector (below) cosines with — non-NULL so
|
||
# the chunk is eligible for the vector list too.
|
||
for c in q38.chunks:
|
||
c.embedding = _vec(1)
|
||
db.commit()
|
||
|
||
q36 = _doc(
|
||
db,
|
||
"deploy",
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.6-27b-juggernaut-vulkan.container",
|
||
"qwen3.6-27b-juggernaut-vulkan",
|
||
"# llama.cpp juggernaut\n-m /models/qwen3.6-27b/model.gguf\n",
|
||
)
|
||
c36 = _chunk(db, q36, 0, "# llama.cpp juggernaut\n-m /models/qwen3.6-27b/model.gguf")
|
||
c36.embedding = _vec(0) # orthogonal to the question vector
|
||
db.commit()
|
||
|
||
other = _doc(
|
||
db, "homelab", "notes/llama.cpp.md", "llama.cpp notes", "llama cpp server arguments notes\n"
|
||
)
|
||
c_other = _chunk(db, other, 0, "llama cpp server arguments notes")
|
||
c_other.embedding = _vec(1, second=True) # half-parallel to the question
|
||
db.commit()
|
||
|
||
# Name carries the token, ZERO chunks — the exclusion guard.
|
||
_doc(db, "deploy", "qwen3.8-empty.container", "qwen3.8-empty", "(empty file)")
|
||
db.commit()
|
||
yield
|
||
db.execute(text("TRUNCATE chunks, documents"))
|
||
db.commit()
|
||
|
||
|
||
def test_name_hit_chunks_real_sql(kb, db) -> None:
|
||
"""Real Postgres: the projection scan finds the qwen3.8 quadlet
|
||
(the ``qwen38`` stem prefix — the incident's original case) AND
|
||
the ``notes/llama.cpp.md`` doc (the question names "llama.cpp" —
|
||
the dotted token ``llamacpp`` exact-matches the file stem, the
|
||
phase-119 two-class rule). The qwen3.6 sibling (``qwen38`` is not
|
||
a prefix of ``qwen36…``) and the chunk-less name match are
|
||
excluded; the LATERAL fetch hands back the SUMMARY chunk as the
|
||
representative for the quadlet (position −1, is_summary) and chunk
|
||
0 for the single-chunk notes doc."""
|
||
out = _name_hit_chunks(db, INCIDENT_QUESTION)
|
||
assert [rc.document.path for rc in out] == [
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.8-27b-juggernaut-vulkan.container",
|
||
"notes/llama.cpp.md", # the question names it — stem exact (A2)
|
||
]
|
||
rc = out[0]
|
||
assert rc.position == -1 # the summary chunk wins the LATERAL order
|
||
assert rc.is_summary is True
|
||
assert rc.fts_hit is True # the lexical signal — the A8 gate answers
|
||
assert rc.cosine == 0.0 # no vector rank on the name-hit row
|
||
rc_notes = out[1]
|
||
assert rc_notes.position == 0 # chunk 0 (no summary chunk)
|
||
assert rc_notes.is_summary is False
|
||
assert all(rc.name_hit is True for rc in out) # phase 119 — D2 bonus input
|
||
assert "qwen3.8-empty.container" not in [r.document.path for r in out] # chunk-less guard
|
||
|
||
|
||
def test_lexical_candidates_name_hit_leads_real_sql(kb, db) -> None:
|
||
"""The full lexical list on real Postgres: the name hit leads, the
|
||
FTS rows follow (the qwen3.6 and llama.cpp docs both match the
|
||
OR-tsquery on llama|cpp|arguments|… — the pre-incident pollution —
|
||
but the name hit still ranks them behind it)."""
|
||
out = _lexical_candidates(db, INCIDENT_QUESTION, limit=30)
|
||
paths = [rc.document.path for rc in out]
|
||
assert paths[0] == (
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.8-27b-juggernaut-vulkan.container"
|
||
)
|
||
assert paths[1] == "notes/llama.cpp.md" # the second name hit (stem exact)
|
||
# The FTS pollution is still present (the incident's shape) — but
|
||
# behind the name hits, no longer ahead of them.
|
||
assert (
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.6-27b-juggernaut-vulkan.container"
|
||
in paths
|
||
)
|
||
assert all(rc.fts_hit is True for rc in out)
|
||
# Phase 119: the two name-hit representative rows are flagged, the
|
||
# plain FTS rows are not.
|
||
assert out[0].name_hit is True
|
||
assert out[1].name_hit is True
|
||
assert all(not rc.name_hit for rc in out[2:])
|
||
|
||
|
||
def test_retrieve_selects_name_hit_doc_into_top_n(kb, db) -> None:
|
||
"""The product path: hybrid ``retrieve()`` (vector ∪ lexical, RRF
|
||
fused) → ``select_documents`` puts the qwen3.8 quadlet in the
|
||
seeded top-N — the incident's seed miss (the two overview docs
|
||
only) is fixed. The question vector is parallel to the q38 chunk
|
||
embeddings (cosine 1.0), orthogonal to q36 (0.0).
|
||
|
||
Phase 119: the ``name_hit`` flag survives the fusion — the two
|
||
name-hit representative chunks are double hits (vector ∪ lexical),
|
||
and the double-hit merge ORs the flag into the surviving row;
|
||
the plain q36 vector+FTS row stays False."""
|
||
question_vec = _vec(1)
|
||
chunks = retrieve(db, INCIDENT_QUESTION, question_vec)
|
||
docs = select_documents(chunks, n=2)
|
||
assert [d.path for d in docs] == [
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.8-27b-juggernaut-vulkan.container",
|
||
"notes/llama.cpp.md",
|
||
]
|
||
by_path: dict[str, list[bool]] = {}
|
||
for rc in chunks:
|
||
by_path.setdefault(rc.document.path, []).append(rc.name_hit)
|
||
# The q38 doc's summary chunk is the double-hit name hit (flagged);
|
||
# its plain chunk 0 (vector + FTS) is not — the doc has a flagged row.
|
||
assert any(by_path[
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.8-27b-juggernaut-vulkan.container"
|
||
])
|
||
assert any(by_path["notes/llama.cpp.md"]) # its only chunk is the name hit
|
||
assert not any(by_path[
|
||
"reeseapps/ai/deployments/juggernaut/quadlets/qwen3.6-27b-juggernaut-vulkan.container"
|
||
]) # plain vector+FTS — never a name hit
|
||
|
||
|
||
def test_name_hit_limit_real_sql(db) -> None:
|
||
"""Twelve tied name hits (one matched token each — the ``qwen38``
|
||
stem PREFIX; the token must lead the stem, the old mid-stem
|
||
containment no longer matches) — the LATERAL fetch (and the
|
||
output) carries exactly ``NAME_HIT_LIMIT`` winners, catalog
|
||
order."""
|
||
db.execute(text("TRUNCATE chunks, documents"))
|
||
db.commit()
|
||
for i in range(12):
|
||
doc = _doc(
|
||
db, "S", f"quadlets/qwen38-m{i:02d}.container", f"qwen38-m{i:02d}", "llama cpp qwen38\n"
|
||
)
|
||
db.flush()
|
||
c = _chunk(db, doc, 0, f"llama cpp qwen38 doc {i}")
|
||
c.embedding = _vec(2)
|
||
db.commit()
|
||
out = _name_hit_chunks(db, "what are the llama.cpp arguments for qwen 3.8")
|
||
assert len(out) == NAME_HIT_LIMIT
|
||
assert [rc.document.path for rc in out] == [
|
||
f"quadlets/qwen38-m{i:02d}.container" for i in range(NAME_HIT_LIMIT)
|
||
]
|
||
assert all(rc.name_hit is True for rc in out)
|