phase: 94_ls_tree_drilldown
All green. Verification complete. **Phase 94 — `ls` drill-down tree: final verification pass (all 5 tasks were already complete; verified, nothing to fix)** - Verified `ls` 3-level tree (`app/rag/agent.py`): `ls()` sources + summaries, `ls(source)`/`ls(source/folder)` drill-down, 50-line file cap + grep-pointer note, NOT-A-FOLDER teaching refusal - Verified `folder_summaries` (migration 0017, model, `app/rag/folder_summaries.py` generator: `FOLDER_SUMMARY_MODE` marker, fail-soft per folder, ≥2-doc scope + prune) wired change-gated in both sync paths - Verified 10-turn fixture battery verdict recorded in `TOOL_CALLING_TESTING.md` §9 (2026-09-11): turbo PASS 19/19 contract, 98.7 s (−12.5…−13.2 % vs baseline); lite PASS 18/18, 43.6 s (+7.7 %) — accuracy at/above baseline, gate met - `uv run pytest --cov=app --cov-report=term-missing` → 1939 passed, 0 failed; TOTAL coverage **99 %** (folder_summaries.py 100 %) - `uv run ruff check .` → clean; `uv run pyright` → 0 errors, 0 warnings - E2E in isolation: `test_ls_tree_drilldown.py` 3 passed; `test_agent_document_tools` 4, `test_agent_unlimited_tools` 4, `test_harness_aligned_tools` 3, `test_search_tool` 3, `test_grep_regex_teaching` 2, `test_response_to_docs` 4 — all passed (read/grep contracts untouched) - Dedicated folder-summary tests (fail-soft, prune, both sync paths, migration): 46 passed - Completion criteria: all 6 met; working tree holds only phase-94 changes (commit left to harness per protocol) **Next pending phase:** `95_read_truncation_cap`
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""folder_summaries: sync-time lite folder summaries (phase 94, task 01)
|
||||
|
||||
Revision ID: 0017
|
||||
Revises: 0016
|
||||
Create Date: 2026-09-10
|
||||
|
||||
Phase 94 (``ls`` becomes a filesystem-style drill-down tree — TODO.md
|
||||
L4): the LLM lists the synced projects, then drills into folders; each
|
||||
level's listing shows the folder's **subtree summary** — a 1–3 sentence
|
||||
plain-text description of what the folder's documents cover, generated
|
||||
at sync time by the aipi ``lite`` model (the ``KB_OVERVIEW_MODE``
|
||||
contract, ``app.rag.overview``: change-gated by the caller, fail-soft,
|
||||
``FOLDER_SUMMARY_MODE`` marker for the E2E mock). This migration adds
|
||||
the storage — ONE additive, fully reversible table (A13), no other
|
||||
schema change:
|
||||
|
||||
* ``folder_summaries`` — one row per folder with ≥ 2 documents
|
||||
(recursive count — the same set the ``ls`` count rule counts,
|
||||
``00_phase.md``; the ≥ 2 rule and the prune rule are generator
|
||||
policy, ``app.rag.folder_summaries``, not schema constraints):
|
||||
|
||||
* PK ``(source, folder_path)`` — ``source`` is VARCHAR(120) NOT NULL
|
||||
(mirrors ``documents.source``); ``folder_path`` is VARCHAR(1000)
|
||||
NOT NULL (mirrors ``documents.path`` — the source-relative folder
|
||||
prefix). ``folder_path = ""`` is the SOURCE ROOT: the top-level
|
||||
source summary (the whole source's recursive subtree).
|
||||
* ``summary`` — TEXT NOT NULL: the ``lite``-written description
|
||||
(never empty — the generator validates before storing).
|
||||
* ``updated_at`` — TIMESTAMPTZ NOT NULL, server default now() (house
|
||||
style — the ``kb_overview.updated_at`` precedent); the generator
|
||||
always stamps it explicitly on upsert.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "0017"
|
||||
down_revision = "0016"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"folder_summaries",
|
||||
sa.Column("source", sa.String(length=120), primary_key=True),
|
||||
sa.Column("folder_path", sa.String(length=1000), primary_key=True),
|
||||
sa.Column("summary", sa.Text(), nullable=False),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.func.now(),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# The table is the only 0017 artefact — dropping it leaves the 0016
|
||||
# schema byte-identical (A13, fully reversible).
|
||||
op.drop_table("folder_summaries")
|
||||
Reference in New Issue
Block a user