Phase 72 (72_teaching_refusals) — completed under the 2026-09-04 controlled
methodology (owner directive: stop clearing/re-importing the homelab KB per
iteration; measure tool-calling accuracy on a controlled fixture KB, target
>90%).
Real-model gate verdicts (live, configured chat model 'lite', fixture KB):
- Controlled fixture battery (the new methodology's pass condition —
contract accuracy >= 90%): PASS, 4 consecutive runs:
gate: lite PASS turns=10 answered=10 caps=0 tool-turns=10 calls 8/11 executed (73%) contract 11/11 (100%) 2026-09-04 (wall 43.4s)
gate: lite PASS turns=10 answered=10 caps=0 tool-turns=10 calls 8/13 executed (62%) contract 12/13 (92%) 2026-09-04 (wall 50.6s)
gate: lite PASS turns=10 answered=10 caps=0 tool-turns=10 calls 7/11 executed (64%) contract 11/11 (100%) 2026-09-04 (wall 46.8s)
gate: lite PASS turns=10 answered=10 caps=0 tool-turns=10 calls 9/15 executed (60%) contract 14/15 (93%) 2026-09-04 (wall 54.8s)
- Locked derived battery (phase-72 task 05, executed >= 90% bar, run
unchanged on the same fixture KB):
gate: lite FAIL turns=10 answered=10 caps=0 tool-turns=10 calls 5/15 executed (33%) contract 12/15 (80%) 2026-09-04 (wall 47.7s)
The teaching works — every bare-path trap self-corrects in exactly one
round, zero cap hits, zero repeat loops, 10/10 answered. The locked
executed bar is blocked by ALREADY_IN_CONTEXT dedupe refusals on the
corrected re-reads (the trap question seeds its target, so the correct
combined-form read is refused for redundancy) — a copy-invariant model
behavior (five copy variants, 0/15 re-reads flipped, 2026-09-03 -> 04)
and an app-semantics decision for the owner (TOOL_CALLING_TESTING.md
sections 5 and 7), not a copy lever.
Copy changes this phase owns (unit pins updated to follow):
- app/rag/agent.py: ls teaching refusals (path-like scope -> document-path
line; unknown source -> no-source line with the source-name
parenthetical), read/grep 'did you mean source/path?' teaching
(find_path_candidates: exact or suffix path match, catalog order, cap 3),
ALREADY_IN_CONTEXT naming the correct action (answer from the text
already in the prompt), read tool description front-loaded with the
do-not-read rule (the 2026-09-04 controlled telemetry: the re-read is
the only remaining refusal class; contract accuracy 92-100% across runs)
- app/rag/prompts.py: TOOLS_SECTION states the document-identity contract
up front (ls path = source name; read/grep = combined source/path
including the source name; do-not-read for <documents> documents placed
next to the read teaching; one-call-per-reply and never-repeat rules)
- tests: refusal pins (unit + integration), new dedicated E2E suite
tests/e2e/test_tool_path_teaching.py (mock misuse flow, green in
isolation), regression suites green in isolation (harness_aligned_tools,
agent_document_tools, agent_unlimited_tools, search_tool, chat_rag).
Gates: uv run pytest green (1501); coverage TOTAL 99% (>90%); ruff +
pyright clean. Carries the still-uncommitted phase-71 todo/ -> complete/
move and both phases' .agent/reports/ (AGENTS.md 8).