phase: 94_ls_tree_drilldown
Build and Push Containers / build-and-push-app (push) Successful in 1m45s
Build and Push Containers / build-and-push-db (push) Successful in 25s

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:
2026-09-11 00:59:35 -04:00
parent 9188be259b
commit d4943b4822
61 changed files with 6289 additions and 666 deletions
+30 -22
View File
@@ -11,11 +11,15 @@ And the phase-71 deflection plain-text line (owner-permitted
line; the ``DEFLECT_MODE`` marker-keying contract is unchanged and
the line never leaks into the HIGH prompt.
And the phase-72 ``<tools>`` copy: the document-identity contract is
stated up front (the ``ls`` source-name scope, the combined
``source/path`` identity for ``read``/``grep``) — the same contract
the teaching refusals in :mod:`app.rag.agent` re-state; the
``<tools>`` marker keying (HIGH only) is unchanged.
And the ``<tools>`` copy (phase 72: the document-identity contract
stated up front — the combined ``source/path`` identity for
``read``/``grep``; phase 94, task 03: the ``ls`` clause rewritten to
the drill-down tree contract — one level per call, sources at the
top, folders + files below, ``grep`` as the without-listing locator —
while the ``read``/``grep`` clauses and the discipline rules are
byte-identical): the teaching refusals in :mod:`app.rag.agent`
re-state the same contract; the ``<tools>`` marker keying (HIGH
only) is unchanged.
"""
from __future__ import annotations
@@ -194,20 +198,22 @@ pinned byte-for-byte in
def test_tools_section_phase72_contract_clauses() -> None:
"""Phase 72: the two contract clauses the teaching refusals
"""Phase 72 + phase 94: the contract clauses the teaching refusals
re-state after the fact, pinned byte-for-byte in the constant —
the ``ls`` source-name clause (its optional ``path`` is a source
name, not a directory or file path; omit it to list every
document) and the ``read``/``grep`` combined-identity clause
(the combined ``source/path`` string exactly as shown in the
``ls`` output, *including the source name*; a bare document path
will not resolve)."""
# The ls source-name clause.
assert (
"a source name (e.g. 'homelab'), not a directory or file "
"path — omit it to list every document"
) in TOOLS_SECTION
# The read combined-identity clause.
the ``ls`` clause (phase 94: the drill-down tree contract — one
level per call, sources at the top, folders + files below, never
the whole KB in one call, ``grep`` as the without-listing locator)
and the ``read``/``grep`` combined-identity clause (the combined
``source/path`` string exactly as shown in the ``ls`` output,
*including the source name*; a bare document path will not
resolve)."""
# The ls drill-down clauses (phase 94, task 03).
assert "one level at a time" in TOOLS_SECTION
assert "lists every synced source with its document count" in TOOLS_SECTION
assert "that source's top-level folders and files" in TOOLS_SECTION
assert "never the whole knowledge base in one call" in TOOLS_SECTION
assert "to find one specific document without listing, use `grep`" in TOOLS_SECTION
# The read combined-identity clause (byte-identical across phases).
assert (
"combined `source/path` string, exactly as shown in the `ls` "
"output — including the source name"
@@ -217,23 +223,25 @@ def test_tools_section_phase72_contract_clauses() -> None:
"a bare document path (without the source name) will not resolve"
) == 2
# The pre-phase-70 scope wording is gone — replaced by the
# explicit source-name contract.
# explicit source-name contract (and the phase-72 source-name-only
# clause by the phase-94 drill-down contract).
assert "pass a source name as `path`" not in TOOLS_SECTION
assert "not a directory or file path" not in TOOLS_SECTION
def test_tools_section_phase72_clauses_in_high_prompt_not_low() -> None:
"""Phase 72: the contract clauses ride the HIGH prompt with the
"""Phase 72/94: the contract clauses ride the HIGH prompt with the
rest of the section and never leak into the LOW/deflection prompt
(whose byte-identity is pinned in
:func:`test_zero_note_prompt_is_byte_identical_to_pre_steering`)."""
doc = _doc("kubernetes.md", "Talos Linux on three nodes.", "Kubernetes Homelab Cluster")
high = build_high_prompt([doc])
assert "<tools>" in high
assert "not a directory or file path" in high
assert "one level at a time" in high # the phase-94 ls clause
assert "including the source name" in high
for low in (build_deflect_prompt(["T1"]), build_deflect_prompt([])):
assert "<tools>" not in low
assert "not a directory or file path" not in low
assert "one level at a time" not in low
assert "including the source name" not in low