phase: 142_kb_overview_freshness
All verification complete. Final report: **Phase 142 final verification pass — all green** - **Verified implemented** (working tree matches design; app diff is exactly 2 lines of logic): `app/rag/overview.py` — `regenerate_overview` now orders the lite-model document list `created_at` desc, `(source, path)` tie-break (newest docs survive the cap); `app/rag/prompts.py` — `_KB_INTRO` rewritten to the honest intro (recency bias, "map not inventory", verify-with-`ls`/`grep`; "(generated at import time)" gone). - **Completion criteria:** 1. Forced-cap outline leads with newest docs — ✅ E2E `test_kb_overview_freshness.py` test 1 (cap 300, digest from `zz_new/z.md`, no "old" tokens) + integration repro (cap 150 → `mydocs zz new 03 md zz new 03`) 2. New honest intro served in `<knowledge_base>` — ✅ unit pin (verbatim) + E2E test 2 (`build_kb_section` surface, tag/cap byte-stable) 3. Gates — ✅ `uv run pytest --cov=app --cov-report=term-missing`: **3359 passed**, `app/` TOTAL **99%** (>90%); `uv run pytest tests/e2e/test_kb_overview_freshness.py -v --no-cov`: **3 passed** in isolation; `uv run pytest tests/e2e/test_kb_overview.py -v --no-cov`: **4 passed** in isolation; `uv run ruff check . && uv run pyright`: **clean, 0 errors** 4. No behavior change in completed phases — ✅ cap env knob, change-gated sync trigger, `build_kb_section` cap/marker, deflect path all byte-identical (pinned pins pass in the 3359) - **Documentation:** README.md reviewed in full (every `.env.example` var documented with matching defaults, all endpoints/routes verified against `app/api/`, Quick Start/deploy/troubleshooting claims verified) and AGENTS.md re-reviewed (rules + quick reference accurate) — **verified consistent, no changes needed**. - **Decisions:** none required — no defects found; no live-infra changes (only the already-running local `db` container was used by tests). - **Next pending phase:** `.agents/phases/todo/143_mobile_keyboard_after_answer`.
This commit is contained in:
@@ -5,6 +5,11 @@ lite-generated KB outline): its own builder contract (empty → ``""``,
|
||||
char budget + ``[…truncated…]`` marker, pathological budgets), its
|
||||
placement between ``<relevance>`` and ``<tuning>`` in both modes, and
|
||||
the byte-identical-when-absent convention (phase 15 precedent).
|
||||
(Phase 142, task 03: the section's intro is the honest-copy rewrite —
|
||||
a compact, possibly partial map that favors recent documents, with the
|
||||
verify-with-``ls``/``grep`` teaching for "latest / complete set"
|
||||
questions — pinned verbatim; the false "(generated at import time)"
|
||||
phrase is gone.)
|
||||
|
||||
And the phase-71 deflection plain-text line (owner-permitted
|
||||
2026-09-03): the LOW prompt = pre-phase text + exactly the one new
|
||||
@@ -60,8 +65,19 @@ from app.rag.retriever import TRUNCATION_MARKER
|
||||
#: A small multi-line outline standing in for the lite-generated one.
|
||||
OVERVIEW = "- Homelab\n - Kubernetes (k3s)\n- Deployments\n - Borg backups"
|
||||
|
||||
#: The locked one-line intro of the ``<knowledge_base>`` section.
|
||||
KB_INTRO = "The basic categories of everything in this knowledge base (generated at import time):"
|
||||
#: The locked one-line intro of the ``<knowledge_base>`` section
|
||||
#: (phase 142, task 03 — the honest-intro rewrite, pinned verbatim in
|
||||
#: :func:`test_kb_intro_phase142_honest_copy`) — the intro line WITHOUT
|
||||
#: its trailing newline (the section renderers / the f-strings below
|
||||
#: supply the line break after it, as the pre-phase-142 constant did).
|
||||
KB_INTRO = (
|
||||
'A compact picture of the knowledge base\'s categories and topics — '
|
||||
'it favors recent documents and may omit older or lesser ones: a map '
|
||||
'of what exists, not a complete inventory. For questions about the '
|
||||
'latest state or the complete set ("the last N …", "everything '
|
||||
'about …", "all the …"), verify with `ls` (drill into the folders) '
|
||||
'or `grep` before answering from the outline:'
|
||||
)
|
||||
|
||||
|
||||
#: The fixture documents' fixed creation date (phase 106, D5) — the
|
||||
@@ -784,6 +800,55 @@ def test_kb_section_format_intro_and_content() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_kb_intro_phase142_honest_copy() -> None:
|
||||
"""Phase 142 (task 03): the intro is pinned verbatim — it says what
|
||||
the outline actually is (a compact picture of the KB's categories
|
||||
and topics that favors recent documents and may omit older or lesser
|
||||
ones — a map of what exists, not a complete inventory) and gives the
|
||||
one behavior that fixes the "the last N phases" question class: for
|
||||
latest-state / complete-set questions, verify with ``ls`` (drill
|
||||
into the folders) or ``grep`` BEFORE answering from the outline. The
|
||||
"(generated at import time)" phrase is gone — it was false (the sync
|
||||
regenerates the row on every KB change) and it taught the model to
|
||||
treat a partial outline as the whole frozen KB."""
|
||||
from app.rag.prompts import _KB_INTRO
|
||||
|
||||
# The app's constant, pinned verbatim (the trailing newline included):
|
||||
assert _KB_INTRO == (
|
||||
'A compact picture of the knowledge base\'s categories and topics — '
|
||||
'it favors recent documents and may omit older or lesser ones: a map '
|
||||
'of what exists, not a complete inventory. For questions about the '
|
||||
'latest state or the complete set ("the last N …", "everything '
|
||||
'about …", "all the …"), verify with `ls` (drill into the folders) '
|
||||
'or `grep` before answering from the outline:\n'
|
||||
)
|
||||
# The local pin is the same text minus the trailing newline.
|
||||
assert KB_INTRO + "\n" == _KB_INTRO
|
||||
# The false claim is gone from the constant (and thus the prompt).
|
||||
assert "(generated at import time)" not in KB_INTRO
|
||||
assert "generated at import time" not in KB_INTRO
|
||||
# The three contracts it must carry: the recency bias (task 02's
|
||||
# newest-first input is what makes it true), the map-not-inventory
|
||||
# framing, and the verify-first teaching with both tool names.
|
||||
assert "favors recent documents" in KB_INTRO
|
||||
assert "not a complete inventory" in KB_INTRO
|
||||
assert "verify with `ls` (drill into the folders) or `grep`" in KB_INTRO
|
||||
assert "before answering from the outline" in KB_INTRO
|
||||
# It rides the rendered section exactly where the old intro did:
|
||||
# between the open tag and the outline, colon-terminated.
|
||||
section = build_kb_section(OVERVIEW)
|
||||
assert section.startswith(f"<knowledge_base>\n{KB_INTRO}")
|
||||
assert section.index(KB_INTRO) < section.index(OVERVIEW)
|
||||
# And it reaches BOTH modes' prompts (the section is mode-agnostic).
|
||||
doc = _doc("kubernetes.md", "CONTENT", "T", summary="S.")
|
||||
for prompt in (
|
||||
build_high_prompt([doc], kb_overview=OVERVIEW),
|
||||
build_deflect_prompt(["T1"], kb_overview=OVERVIEW),
|
||||
):
|
||||
assert KB_INTRO in prompt
|
||||
assert "(generated at import time)" not in prompt
|
||||
|
||||
|
||||
def test_kb_section_trims_overview_edges() -> None:
|
||||
assert build_kb_section(f" {OVERVIEW} \n") == build_kb_section(OVERVIEW)
|
||||
|
||||
@@ -797,9 +862,11 @@ def test_kb_section_fits_budget_exactly_no_marker() -> None:
|
||||
|
||||
def test_kb_section_over_budget_capped_with_marker() -> None:
|
||||
text = "- " + "x" * 500
|
||||
# The section frame alone is 121 chars, so the cap must clear it for
|
||||
# any outline prefix to fit (pathological budgets are tested below).
|
||||
cap = 200
|
||||
# The section frame alone is 422 chars (the tag + the phase-142
|
||||
# honest intro + the marker + the close tag), so the cap must clear
|
||||
# it for any outline prefix to fit (pathological budgets are tested
|
||||
# below).
|
||||
cap = 600
|
||||
section = build_kb_section(text, max_chars=cap)
|
||||
assert len(section) <= cap # the budget is never exceeded
|
||||
assert TRUNCATION_MARKER in section
|
||||
@@ -961,8 +1028,8 @@ def test_prompt_kb_section_over_settings_budget_capped_with_marker(
|
||||
monkeypatch.setattr(
|
||||
prompts_mod,
|
||||
"get_settings",
|
||||
# 200 > the 121-char section frame, so a prefix + marker can fit.
|
||||
lambda: Settings(_env_file=None, kb_overview_max_chars=200), # pyright: ignore[reportCallIssue]
|
||||
# 600 > the 422-char section frame, so a prefix + marker can fit.
|
||||
lambda: Settings(_env_file=None, kb_overview_max_chars=600), # pyright: ignore[reportCallIssue]
|
||||
)
|
||||
text = "- " + "z" * 500
|
||||
doc = _doc("kubernetes.md", "TALOS_DOC_CONTENT", "Kubernetes Homelab Cluster")
|
||||
@@ -976,7 +1043,7 @@ def test_prompt_kb_section_over_settings_budget_capped_with_marker(
|
||||
section = prompt[prompt.index("<knowledge_base>") :]
|
||||
close = section.index("</knowledge_base>")
|
||||
section = section[: close + len("</knowledge_base>")]
|
||||
assert len(section) <= 200
|
||||
assert len(section) <= 600
|
||||
|
||||
|
||||
# ---------- phase 71: the deflection plain-text line (prevention) ----------
|
||||
|
||||
Reference in New Issue
Block a user