phase: 131_read_line_ranges
Build and Push Containers / build-and-push-app (push) Successful in 2m30s
Build and Push Containers / build-and-push-db (push) Successful in 11s

All 44 progress lines are pure dots, final line at 100%, exit 0. Everything is verified. Final report:

**Phase 131 — final verification pass (all 4 tasks already complete in working tree)**

- **Verified implemented:** `read(offset, limit)` schema + teaching descriptions; rewritten truncation notice; full 6-row dedupe matrix + range validation + `lines: a-b of N` header + slice cap in `_execute_tool`; wire-level `offset`/`limit` on `ChatToolEvent` (absent keys dropped → non-range frames byte-identical); mock-LLM `read the lines` flow; handbook repro (wording + line numbers derived from the real PDF at test time)
- **Tests:** `uv run pytest --no-cov` → 3097 passed, exit 0; `uv run pytest --cov=app` → **99% on app/** (>90%); `uv run pytest tests/e2e/test_read_line_ranges.py -v --no-cov` → 2/2 in isolation (47.6 s); `tests/integration/test_read_line_ranges_handbook.py` → 2/2 (3.5 s); 10-file targeted E2E batch pinning pre-phase surfaces (grep/ls/plain-read/ALREADY_IN_CONTEXT/truncation) → 31/31; `ruff check .` clean; `pyright` 0 errors
- **Completion criteria:** all 5 green — (1) exact range lines + `lines:` header, plain reads byte-identical (existing pins pass unchanged) ✅ (2) truncated-then-range succeeds end-to-end on the real handbook; other re-reads get teaching refusals ✅ (3) find-traps answered from real PDF text, no hardcoded wording ✅ (4) coverage + isolated E2E ✅ (5) no behavior change in completed phases ✅
- **Docs:** README + AGENTS reviewed in full. Fixed 3 inconsistencies: README missing `BOR_HISTORY_MAX_TURNS`/`BOR_HISTORY_MAX_CHARS` rows (pre-existing phase-74 gap — env-tunable, unit-pinned, undocumented); `.env.example` missing the same two vars (added); `.env.example` stale `BOR_READ_MAX_CHARS` comment (still said "grep-pointer notice (phase 95)" — updated to the phase-131 workflow notice). AGENTS.md verified consistent — no changes needed. All 67 `.env.example` vars documented, all defaults match `app/config.py`, pages/auth tables match live routes
- **Note:** PLAN.md §7.1 still states the retired 46rem chat column (superseded by phase 100, which AGENTS.md rule 5 records correctly) — PLAN.md is off-limits for this pass, flagged only
- **Next pending phase:** `132_image_upload_feedback`
This commit is contained in:
2026-09-28 11:49:55 -04:00
parent c88c65641a
commit 101156bada
39 changed files with 3521 additions and 108 deletions
+40
View File
@@ -127,6 +127,46 @@ def test_tool_event_shape_is_type_name_argument_only() -> None:
assert dumped["type"] == "tool" # default — call sites never spell it out
def test_tool_frame_range_read_carries_offset_and_limit() -> None:
"""Phase 131 (task 04): a range ``read``'s frame carries the line
range the model requested — the wire's assertion surface for the
owner's grep → read(offset/limit) dance (the story E2E pins the
REAL ints the mock parsed from the grep result)."""
frame = sse_event(
ChatToolEvent(
name="read", argument="S/p.md", offset=1500, limit=30
).model_dump()
)
assert frame == (
'data: {"type": "tool", "name": "read", "argument": "S/p.md", '
'"offset": 1500, "limit": 30}\n\n'
)
assert _payload(frame) == {
"type": "tool",
"name": "read",
"argument": "S/p.md",
"offset": 1500,
"limit": 30,
}
def test_tool_event_range_fields_absent_not_null_when_omitted() -> None:
"""Phase 131 (task 04): the ``offset`` / ``limit`` keys are ABSENT
(never ``null``) when omitted — a plain ``read`` (no range) and a
``grep`` (no range parameters at all) both serialize to the
byte-identical pre-phase shape (the omission pattern of
``ChatDoneEvent.answer_image``). A bad range VALUE never reaches
the frame at all: ``app.api.chat`` applies the whole-line-number
guard before building the event (the E2E suite pins the frame the
endpoint emits)."""
for dumped in (
ChatToolEvent(name="read", argument="S/p.md").model_dump(),
ChatToolEvent(name="read", argument="S/p.md", offset=None, limit=None).model_dump(),
ChatToolEvent(name="grep", argument="trap").model_dump(),
):
assert set(dumped.keys()) == {"type", "name", "argument"}, dumped
def test_retry_frame_serializes_exactly() -> None:
"""Phase 67: the ``retry`` frame is exactly ``{type: "retry",
attempt: int, max_attempts: int}`` — a transient status the client's