feat(rag): summarize single-document folders (MIN_DOCS_PER_FOLDER 2 → 1)
Relax the phase-94 folder-summary scope rule from ≥ 2 documents to ≥ 1: a folder (or source root) is a candidate while ANY document lives under it, so single-file folders and single-file source roots get their own lite-written description. A row is now pruned only when its folder loses its last document (vanishes from the catalogue). The constant is the single source of truth, so the flip propagates to the generator's candidate set, the prune pass, the missing_folder_summaries gap probe (the next sync self-heals the new gaps), and the KB-tree summary_pending markers (1-doc folders / sources now read "Summary pending" until their row lands). Docstrings/comments across app/, scripts/import_docs.py, and the E2E fixtures updated to the ≥ 1 wording. Unit + integration tests updated to the new semantics (the pruned-below-minimum scenario is now a folder losing its LAST doc; single-doc folders are pinned as candidates/pending). Full suite: 2314 passed, app coverage 99%; ruff + pyright clean; folder-summary E2E stories pass in isolation (ls_tree_drilldown, sync_summary_visibility, kb_tree, kb_tree_nav, document_dates, oneshot_llm_retry).
This commit is contained in:
@@ -68,7 +68,8 @@ The stored folder summaries (phase 94 — the drill-down ``ls``'s
|
||||
per-level descriptions, the ``folder_summaries`` table) regenerate in
|
||||
the same run under the same gate: a changed KB (added + updated > 0 —
|
||||
full regeneration), or, after an unchanged walk, a GAP — a candidate
|
||||
folder (≥ 2 docs) with no stored row (phase 96: this subsumes the old
|
||||
folder (≥ 1 doc — every existing folder) with no stored row
|
||||
(phase 96: this subsumes the old
|
||||
table-empty trigger exactly — an empty table leaves EVERY candidate
|
||||
missing, as after the first full run after migration 0017 or a
|
||||
``--limit`` first walk that skipped them — and catches the single row
|
||||
@@ -282,7 +283,8 @@ def _overview_row_exists() -> bool:
|
||||
|
||||
def _folder_summaries_gap() -> list[tuple[str, str]]:
|
||||
"""The folder-summary gaps (phase 96, task 03): the candidate
|
||||
folders (≥ 2 docs) with no stored row, sorted.
|
||||
folders (≥ 1 doc — every existing folder) with no stored row,
|
||||
sorted.
|
||||
|
||||
The unchanged-walk self-heal trigger, replacing the phase-94
|
||||
table-emptiness probe (which the gap subsumes exactly: an empty
|
||||
@@ -358,8 +360,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
The folder summaries (phase 94, task 02; phase 96, task 03)
|
||||
follow the same gate — a changed KB (full regeneration), or,
|
||||
after an unchanged walk, a GAP: a candidate folder (≥ 2 docs)
|
||||
with no stored row (the subsumed table-empty trigger — an
|
||||
after an unchanged walk, a GAP: a candidate folder (≥ 1 doc —
|
||||
every existing folder) with no stored row (the subsumed
|
||||
table-empty trigger — an
|
||||
empty table leaves every candidate missing — plus a row an
|
||||
exhausted one-shot retry lost) — with per-folder fail-soft
|
||||
inside the generator (a ``lite`` failure keeps the failed
|
||||
|
||||
Reference in New Issue
Block a user