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:
+30
-9
@@ -1,10 +1,23 @@
|
||||
"""KB overview generator (phase 31, task 02).
|
||||
|
||||
Builds the ``KB_OVERVIEW_MODE`` prompt from the document catalogue
|
||||
(source, path, title, first summary line per document), calls the aipi
|
||||
``lite`` model through the one-shot ``LLMClient.chat`` (phase 30, same
|
||||
model as document summaries — no new model management), and stores the
|
||||
plain-text outline in the single ``kb_overview`` row (migration 0005).
|
||||
(one line per document: source, path, title, first summary line —
|
||||
ordered RECENT-FIRST by the document's date, phase 142 task 02),
|
||||
calls the aipi ``lite`` model through the one-shot ``LLMClient.chat``
|
||||
(phase 30, same model as document summaries — no new model management),
|
||||
and stores the plain-text outline in the single ``kb_overview`` row
|
||||
(migration 0005).
|
||||
|
||||
The recency-first order is what makes the input cap honest: when
|
||||
``BOR_OVERVIEW_INPUT_MAX_CHARS`` cuts the list, the documents that fall
|
||||
off are the OLDEST, so the outline always represents the recent state
|
||||
of the KB. The sort reads ``Document.created_at`` — phase 106's
|
||||
git-history/mtime-derived, owner-correctable document date (the same
|
||||
date the ``read`` tool's ``date:`` line shows); ``created_at_manual``
|
||||
rows are included exactly as stored, the ordering never re-derives
|
||||
dates. Same-date documents tie-break by ``(source, path)``, keeping
|
||||
the pre-phase-142 lexicographic order within a date (deterministic for
|
||||
the mock's digest; byte-identical for single-date KBs).
|
||||
|
||||
Chat turns never generate an overview — they read the stored row
|
||||
(:func:`load_kb_overview`, one indexed PK lookup) and inject it into the
|
||||
@@ -99,10 +112,12 @@ def build_overview_prompt(
|
||||
+ the locked instruction.
|
||||
* ``user`` — one line per document,
|
||||
``source — path — title — {first line of summary}``, joined with
|
||||
newlines. The summary field is omitted when the document has no
|
||||
summary (pre-phase-30 rows, the fail-soft path, and pre-backfill
|
||||
NULL rows — no dangling dash). The list is capped at *max_chars*
|
||||
(default
|
||||
newlines, in the EXACT order the caller passes *rows* (order-
|
||||
preserving; the caller — :func:`regenerate_overview` — passes the
|
||||
catalogue recent-first, phase 142 task 02). The summary field is
|
||||
omitted when the document has no summary (pre-phase-30 rows, the
|
||||
fail-soft path, and pre-backfill NULL rows — no dangling dash).
|
||||
The list is capped at *max_chars* (default
|
||||
``BOR_OVERVIEW_INPUT_MAX_CHARS``): overflow is cut exactly at the
|
||||
cap and the shared ``[…truncated…]`` marker is appended on its own
|
||||
line, so the model never sees more than the cap and the cut is
|
||||
@@ -163,9 +178,15 @@ async def regenerate_overview(
|
||||
if session is None:
|
||||
session = SessionLocal()
|
||||
try:
|
||||
# RECENT-FIRST (phase 142, task 02): the document's date
|
||||
# (``created_at``, phase 106) descending, so when the input cap
|
||||
# bites the OLDEST documents fall off — the outline always
|
||||
# represents the recent KB. The (source, path) tie-break keeps
|
||||
# same-date documents in the pre-phase-142 lexicographic order
|
||||
# (deterministic digest; byte-identical for single-date KBs).
|
||||
result = session.execute(
|
||||
select(Document.source, Document.path, Document.title, Document.summary)
|
||||
.order_by(Document.source, Document.path)
|
||||
.order_by(Document.created_at.desc(), Document.source, Document.path)
|
||||
).all()
|
||||
rows: list[tuple[str, str, str, str | None]] = [
|
||||
(source, path, title, summary)
|
||||
|
||||
+16
-4
@@ -125,11 +125,23 @@ _STEERING_INTRO = (
|
||||
"Where these instructions conflict with the defaults above, follow the owner:\n"
|
||||
)
|
||||
|
||||
#: One-line intro of the ``<knowledge_base>`` section (phase 31): the
|
||||
#: lite-generated outline is the agent's a-priori picture of the KB.
|
||||
#: One-line intro of the ``<knowledge_base>`` section (phase 31;
|
||||
#: phase 142, task 03 — the honest-intro rewrite, owner-confirmed
|
||||
#: 2026-09-30): the outline is a COMPACT, possibly partial picture
|
||||
#: that favors RECENT documents (task 02 orders the generation input
|
||||
#: newest-first, so recent docs are the ones it covers when the cap
|
||||
#: bites) — a map of what exists, not a complete inventory — and
|
||||
#: "latest / complete set" questions verify with ``ls``/``grep``
|
||||
#: before answering from it; "(generated at import time)" is GONE
|
||||
#: (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).
|
||||
_KB_INTRO = (
|
||||
"The basic categories of everything in this knowledge base "
|
||||
"(generated at import time):\n"
|
||||
'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 ``<documents>`` section's start-here intro (phase 118, task 03 —
|
||||
|
||||
Reference in New Issue
Block a user