phase: 113_source_chip_quality
Build and Push Containers / build-and-push-app (push) Successful in 2m2s
Build and Push Containers / build-and-push-db (push) Successful in 15s

All gates green — no defects found; this pass was verification only.

**Phase 113 final verification pass — report**

- Verified (no code changes needed): `select_documents_tiered` cited/related tiering + `select_documents` wrapper, `TurnPlan.related_docs`, `ChatDoneEvent.related` (additive, old payloads parse), `appendRelated` UI row (`.related-doc`, never `.source-chip`), done-frame + restore-path wiring, two settings with validators, `.env.example` entries
- `uv run pytest --cov=app --cov-report=term-missing` → 2422 passed, app/ coverage **99%** (>90% gate)
- `uv run pytest tests/e2e/test_source_chip_quality.py -v --no-cov` (isolated) → 2 passed
- Regression E2E `test_retrieval_quality.py` + `test_honest_deflection.py` + `test_chat_rag.py` + `test_sources_midstream_bug.py` → 17 passed
- `uv run ruff check . && uv run pyright` → clean (0 errors); `bash .agents/validate.sh` → "validation OK"

Completion criteria:
1. Single-doc question → exactly one `.source-chip` (E2E): ✅ passed
2. Weak 2nd doc only in de-emphasized related row, never `.source-chip` (unit + E2E): ✅ passed
3. Deflected turn → zero citation chips, weak hits in related row: ✅ passed
4. Full suite green, coverage >90%, isolated E2E green, lint/types clean: ✅ passed
5. `--no-gpg-sign` commit + phase dir move: left to harness per pass rules (task files already in `complete/`)

No deviations. Next pending phase: `114_embed_question_length`.
This commit is contained in:
2026-09-15 03:11:05 -04:00
parent 1374faf136
commit 97d663d16d
31 changed files with 2370 additions and 52 deletions
+69
View File
@@ -213,6 +213,75 @@ def test_agent_max_rounds_rejects_negative(monkeypatch: pytest.MonkeyPatch) -> N
_settings()
def test_source_usefulness_floor_default_and_env_override(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Phase 113 (LOCKED A2): the citation-slot bar — default 0.35 (the
same bar as the A8 lexical support floor), env-tunable, ``0`` = the
no-bar kill switch."""
monkeypatch.delenv("BOR_SOURCE_USEFULNESS_FLOOR", raising=False)
# The test process pins the mock-calibrated threshold (0.30, see
# tests/conftest.py) — clear it so the PRODUCTION default pair
# (0.62 / 0.35) is what the validator sees.
monkeypatch.delenv("BOR_RELEVANCE_THRESHOLD", raising=False)
assert _settings().source_usefulness_floor == 0.35
monkeypatch.setenv("BOR_SOURCE_USEFULNESS_FLOOR", "0.5")
assert _settings().source_usefulness_floor == 0.5
monkeypatch.setenv("BOR_SOURCE_USEFULNESS_FLOOR", "0")
assert _settings().source_usefulness_floor == 0.0
def test_source_usefulness_floor_at_threshold_is_legal(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The validator bound is inclusive (>=): a bar exactly at the
relevance threshold is legal — the gate and the bar agree on every
grounded document."""
monkeypatch.delenv("BOR_SOURCE_USEFULNESS_FLOOR", raising=False)
monkeypatch.delenv("BOR_RELEVANCE_THRESHOLD", raising=False)
s = _settings(relevance_threshold=0.62, source_usefulness_floor=0.62)
assert s.source_usefulness_floor == 0.62
def test_source_usefulness_floor_rejects_above_threshold(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""A bar above the relevance threshold would demote to the related
tier documents the gate itself calls grounded — a typo, so the
validator fails loudly at startup (the lexical_support_floor guard)."""
monkeypatch.delenv("BOR_SOURCE_USEFULNESS_FLOOR", raising=False)
with pytest.raises(ValidationError, match="source_usefulness_floor"):
_settings(relevance_threshold=0.62, source_usefulness_floor=0.70)
def test_source_usefulness_floor_rejects_negative(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""A negative bar is a typo (the agent_max_rounds pattern)."""
with pytest.raises(ValidationError, match="source_usefulness_floor"):
_settings(source_usefulness_floor=-0.1)
def test_related_max_docs_default_and_env_override(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Phase 113 (LOCKED A4): the related-doc tier cap — default 2,
``0`` = the no-related-docs kill switch."""
monkeypatch.delenv("BOR_RELATED_MAX_DOCS", raising=False)
assert _settings().related_max_docs == 2
monkeypatch.setenv("BOR_RELATED_MAX_DOCS", "0")
assert _settings().related_max_docs == 0
def test_related_max_docs_rejects_negative(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""A negative cap is a typo (the agent_max_rounds pattern)."""
monkeypatch.setenv("BOR_RELATED_MAX_DOCS", "-1")
with pytest.raises(ValidationError, match="related_max_docs"):
_settings()
def test_read_max_chars_default_and_env_override(
monkeypatch: pytest.MonkeyPatch,
) -> None: