Files
brain-of-reese/.agents/phases/complete/142_kb_overview_freshness/00_phase.md
T
ducoterra aa65d2d691
Build and Push Containers / build-and-push-app (push) Successful in 2m33s
Build and Push Containers / build-and-push-db (push) Successful in 12s
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`.
2026-10-01 04:25:03 -04:00

7.8 KiB
Raw Blame History

Phase 142 — the KB overview stays fresh: newest documents survive the cap, and the model is told the outline is partial

Source: TODO.md L4 — "The agent is really struggling with questions like 'Summarize the last 10 phases of brain of Reese'. When I asked that to the current deployment it said 'I'll pull the details for the last 10 phases (104–113).' when it clearly had phase 137 loaded in the RAG… 'The <knowledge_base> section in the system prompt only generated summaries up to phase 113 at import time. The phases 128–137 exist as full markdown files… but their summaries weren't included in the initial KB index. This is a KB sync/generation gap, not a retrieval logic bug.'" Story: n/a (TODO-derived phase, Protocol B append). Context: app/rag/overview.py — regenerate_overview (the document-list query order_by(Document.source, Document.path) ~L232, best-effort change-gated per the module docstring), build_overview_prompt (one line per doc source — path — title — {first summary line}, capped at max_chars with the shared TRUNCATION_MARKER), load_kb_overview; app/config.py L216 — overview_input_max_chars (BOR_OVERVIEW_INPUT_MAX_CHARS, default 40 000); app/rag/prompts.py — _KB_INTRO (L128–132: literally says "(generated at import time)") and build_kb_section (L399+); app/api/sync.py ~L590 — the overview regeneration gate (summary.added + summary.updated > 0 — the sync IS change-gated correctly; the staleness is inside the generation, not the trigger); tests/e2e/test_kb_overview.py (the mock's KB_OVERVIEW_MODE digest = first 8 tokens of the document list — ORDER-DEPENDENT, must be re-pinned), tests/integration/test_import_docs_overview.py (the import-time trigger), app/models.py — Document.created_at (L123 — the DOCUMENT's date, phase 106: git-history/mtime-derived and owner-correctable, the same date the read result's date: line shows — the "when is this document from" signal, exactly what "the last N …" means). Root cause (traced): on the owner's 1 000-doc KB the document list is ≈100k+ chars; the 40k cap cuts a LEXICOGRAPHIC prefix (order_by (source, path) — "113_…" sorts before "128_…", and "131_…"–"140_" sort after "13_…"), so the newest phases never reach the lite model and the stored outline confidently describes a KB that ends mid-11x; the model then anchors "the last 10 phases" on that stale image. Locked (owner-confirmed 2026-09-30, chat): recency ordering (newest documents first so they survive the cap; the cap itself is unchanged) + the honest <knowledge_base> intro (drops "(generated at import time)"; tells the model the outline is compact/possibly partial and to verify "latest/complete" questions with ls/grep first). No map-reduce rewrite this phase.

Objective

"Summarize the last 10 phases" stops being a guess: the overview's input is ordered newest-first so recent documents are the ones the outline covers when the cap bites, and the <knowledge_base> intro stops teaching the model that a partial outline is the whole KB — it now says the outline is a possibly-partial picture and points at ls/grep for "what's the latest / the complete set" questions.

Dependencies

  • 141_read_range_dedupe_fix (todo) — pipeline order only; no code dependency.

Design (shared by all tasks — the executor reads this, not the chat)

  • Ordering (task 02): regenerate_overview's query becomes order_by(Document.created_at.desc(), Document.source, Document.path) — newest documents FIRST (created_at is the document's date per phase 106 — the same date the date: line in a read result shows; the owner's phase-128–137 files carry newer git dates than the 113-era files, so they lead the list and survive the cap). The (source, path) secondary keys keep the tie-break deterministic for same-date docs (the common case within one commit/import — those fall back to the old lexicographic order, so same-date fixtures stay byte-identical). The cap (BOR_OVERVIEW_INPUT_MAX_CHARS, default 40 000) and the cap's cut+TRUNCATION_MARKER behavior are UNCHANGED (house truncation convention; the env knob stays the owner's lever). Zero other call-site changes: the sync's change-gate (task-31/32 trigger) already regenerates whenever the KB changes — the staleness was purely inside the list.
  • Intro (task 03): _KB_INTRO rewritten (it is a prompt constant — PLAN §4's convention: persona/prompt text changes ship with the phase's prompt tests; the <knowledge_base> TAG and the section's cap behavior in build_kb_section stay byte-stable): the outline is a COMPACT, possibly partial picture of the KB that favors RECENT documents — and for questions about the latest state or the complete set ("the last N …", "everything about …"), verify with ls (drill down) / grep before answering from the outline. One short paragraph, same tone as SUGGEST_INTRO (L152).
  • Repro (task 01): before any fix, a failing test proves the traced root cause: build_overview_prompt given a doc list whose joined lines exceed max_chars keeps the lexicographic FIRST lines and drops the last; and (integration) a KB where the newest docs' paths sort lexicographically LAST is regenerated with the pre-fix ordering → the stored outline (the mock's 8-token digest) names the OLD docs, not the new ones. The repro is written against the CURRENT code (red), then tasks 02/03 flip it green.
  • E2E (task 04): dedicated file — a fixture KB whose document count × line length exceeds a FORCED small cap (BOR_OVERVIEW_INPUT_MAX_CHARS set low via the E2E settings fixture) → regenerate through the mock → the stored outline's digest tokens come from the NEWEST document (recency ordering visible end-to-end through the real prompt path), and the served chat prompt's <knowledge_base> section carries the NEW intro copy (asserted from the echo path or the direct prompt-build surface — the phase-31 E2E already exposes the section to the UI via the first-bullet echo).

Tasks

  1. 01_cap_ordering_repro.md — the red repro tests (unit: cap keeps the lexicographic prefix; integration: newest-sorting-last docs missing from the regenerated outline).
  2. 02_recency_ordering.md — the created_at.desc() ordering + the order-dependent digest re-pins.
  3. 03_honest_kb_intro.md — the _KB_INTRO rewrite + the prompt tests.
  4. 04_e2e_outline_freshness.md — the dedicated E2E (forced cap → newest doc in the outline; the new intro served).

Testing & Quality

  • Unit: build_overview_prompt cap behavior unchanged (the cut + marker, the pathological budget); the new ordering (regeneration with mixed created_at docs → newest first; same-date tie-break by (source, path)); the intro copy pin.
  • Integration: the import-time trigger still regenerates exactly on KB change (the pre-existing tests/integration/test_import_docs_overview.py green); the repro flipped green.
  • E2E: one dedicated Playwright file, run in isolation, mock-only; tests/e2e/test_kb_overview.py (phase 31) green with re-pinned digest.
  • Coverage: >90% on new/modified code (app/).

Completion Criteria

  • with the cap forced, the regenerated outline is built from the NEWEST documents (the 1 000-doc "ends at phase 113" failure mode is gone: the newest sort-last docs are the ones represented)
  • the served <knowledge_base> section carries the new honest intro (no "(generated at import time)"; the verify-with-ls/grep teaching present)
  • uv run pytest --cov=app --cov-report=term-missing green, app/ coverage >90%; uv run pytest tests/e2e/test_kb_overview_freshness.py -v --no-cov green in isolation; tests/e2e/test_kb_overview.py still green in isolation; uv run ruff check . && uv run pyright clean
  • no behavior change in completed phases (the cap env knob, the change-gated trigger, the build_kb_section cap/marker behavior, the deflect path)