phase: 131_read_line_ranges
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user