phase: 113_source_chip_quality
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:
@@ -126,6 +126,29 @@ class Settings(BaseSettings):
|
||||
# ``relevance_threshold`` (a floor above the threshold is a typo that
|
||||
# would make every FTS hit require a HIGH cosine anyway).
|
||||
lexical_support_floor: float = 0.35
|
||||
#: Usefulness bar for the citation slot (phase 113, LOCKED A2): a
|
||||
#: retrieved document earns ``done.sources`` (the UI's citation chip)
|
||||
#: only when the **cosine** of its best hit chunk clears this floor —
|
||||
#: the vector signal must corroborate the citation, mirroring the A8
|
||||
#: honesty gate's ``lexical_support_floor``. Documents that scored but
|
||||
#: stay below the bar are demoted to the secondary related-doc tier
|
||||
#: (at most ``related_max_docs``). ``top_n_docs`` is the CEILING for
|
||||
#: the cited tier, never a quota: a single strong document yields one
|
||||
#: citation. Default 0.35 — the same bar as ``lexical_support_floor``;
|
||||
#: tunable via ``BOR_SOURCE_USEFULNESS_FLOOR``. Must satisfy
|
||||
#: ``0 <= source_usefulness_floor <= relevance_threshold`` (a floor
|
||||
#: above the threshold would demote to the related tier documents the
|
||||
#: gate itself calls grounded — the ``lexical_support_floor`` typo
|
||||
#: guard). ``0`` disables the bar (every scored doc is citable — the
|
||||
#: pre-phase behavior, the kill switch).
|
||||
source_usefulness_floor: float = 0.35
|
||||
#: Cap on the secondary related-doc tier (phase 113, LOCKED A4):
|
||||
#: documents that scored but did not clear ``source_usefulness_floor``
|
||||
#: ride the ``done`` frame's ``related`` list (the UI's de-emphasized
|
||||
#: "nearby docs" row — never a citation chip). ``0`` = no related
|
||||
#: docs at all (the kill switch); a negative value fails startup
|
||||
#: loudly (the ``agent_max_rounds`` pattern).
|
||||
related_max_docs: int = 2
|
||||
#: Maximum output tokens a chat answer may use (owner instruction
|
||||
#: 2026-08-22: answers must run to their natural end — the old hard
|
||||
#: 700-token cap cut long answers off mid-sentence).
|
||||
@@ -327,6 +350,31 @@ class Settings(BaseSettings):
|
||||
)
|
||||
return v
|
||||
|
||||
@field_validator("source_usefulness_floor")
|
||||
@classmethod
|
||||
def _source_usefulness_floor_bounds(cls, v: float, info: ValidationInfo) -> float:
|
||||
"""The usefulness bar must be in [0, relevance_threshold]. A value
|
||||
above the relevance threshold would be a typo — it would demote to
|
||||
the related tier documents the honesty gate itself calls grounded
|
||||
(the ``lexical_support_floor`` typo guard, phase 113)."""
|
||||
if v < 0:
|
||||
raise ValueError("source_usefulness_floor must be >= 0")
|
||||
threshold = info.data.get("relevance_threshold")
|
||||
if isinstance(threshold, float) and v > threshold:
|
||||
raise ValueError(
|
||||
f"source_usefulness_floor ({v}) must be <= relevance_threshold ({threshold})"
|
||||
)
|
||||
return v
|
||||
|
||||
@field_validator("related_max_docs")
|
||||
@classmethod
|
||||
def _related_max_docs_non_negative(cls, v: int) -> int:
|
||||
"""``0`` is the no-related-docs kill switch — a negative cap is a
|
||||
typo (the ``agent_max_rounds`` pattern, phase 113)."""
|
||||
if v < 0:
|
||||
raise ValueError("related_max_docs must be >= 0 (0 = no related docs)")
|
||||
return v
|
||||
|
||||
@field_validator("import_extensions")
|
||||
@classmethod
|
||||
def _import_extensions_known(cls, v: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user