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:
+8
-6
@@ -288,12 +288,13 @@ class KbTreeFolder(BaseModel):
|
||||
resolves it with ``from __future__ import annotations``).
|
||||
|
||||
``summary_pending`` (phase 98, D3 — ONE concept): true iff this
|
||||
folder's recursive count ≥ ``MIN_DOCS_PER_FOLDER`` (2) AND it has
|
||||
folder's recursive count ≥ ``MIN_DOCS_PER_FOLDER`` (1) AND it has
|
||||
NO stored ``folder_summaries`` row (AI or manual — any row) —
|
||||
exactly the candidate the sync-time gap-fill regenerates (the
|
||||
:func:`app.rag.folder_summaries.missing_folder_summaries` set).
|
||||
A < 2-document folder is never pending (it never gets a summary —
|
||||
its one file line IS its description).
|
||||
A 0-document folder cannot exist (a folder is a catalogue prefix
|
||||
only), so every existing folder with no stored row is pending —
|
||||
single-file folders included.
|
||||
"""
|
||||
|
||||
kind: Literal["folder"] = "folder"
|
||||
@@ -325,12 +326,13 @@ class KbTreeSource(BaseModel):
|
||||
|
||||
``summary_pending`` (phase 98, D3 — ONE concept): true iff the
|
||||
source's recursive ``documents`` count ≥ ``MIN_DOCS_PER_FOLDER``
|
||||
(2) AND no stored ``(source, "")`` row (AI or manual — any row) —
|
||||
(1) AND no stored ``(source, "")`` row (AI or manual — any row) —
|
||||
exactly the source-root candidate the sync-time gap-fill
|
||||
regenerates (the
|
||||
:func:`app.rag.folder_summaries.missing_folder_summaries` set).
|
||||
A registered 0-document source is never pending (there is nothing
|
||||
to summarize).
|
||||
to summarize); a single-document source IS pending while its root
|
||||
row is absent.
|
||||
"""
|
||||
|
||||
name: str
|
||||
@@ -353,7 +355,7 @@ class KbTree(BaseModel):
|
||||
|
||||
Every SOURCE and FOLDER node carries ``summary_pending`` (phase
|
||||
98, D3): true iff its recursive document count ≥
|
||||
``MIN_DOCS_PER_FOLDER`` (2) AND it has no stored ``folder_summaries``
|
||||
``MIN_DOCS_PER_FOLDER`` (1) AND it has no stored ``folder_summaries``
|
||||
row — exactly ``missing_folder_summaries``'s candidate set (the
|
||||
marker never drifts from the gap-fill); FILE nodes carry no flag
|
||||
(the file table has no description column).
|
||||
|
||||
Reference in New Issue
Block a user