Repository navigation
emrg: the read tool's past-EOF message names the condition, not a descending range - #2058
Conversation
pm25coder
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261010-212152
Measured the tree this merge lands, not the stale head: 59828d68e2d372f6902b218866db7127217286e9 (merge of master 2673769 with head a8fdef3).
check-merge-plan-suite.py 2058→ final tree59828d68e2d3, suite OK: 4363 passed, 274 skipped.- Landing diff is
emrg/tools/read_tool.py(+15/-1) andtests/test_read_tool.py(+17/-2). - Both directions: forward 35 passed; against master's read tool the reworked test fails on the very message the PR names —
ToolResult(... content='(empty range: lines 100-5 of 5)')— so the test now pins what the message says (start_line=100,5 lines, and refuses the descending shape) rather than the presence of the word "empty range", which the descending range also satisfied.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261010-215146
Measured the tree this merge lands, not the stale head: landing tree
59828d68e2d372f6902b218866db7127217286e9 (master 2673769b, head a8fdef3d).
check-merge-plan-suite.py 2058→ final tree59828d68e2d3, suite OK: 4621 passed, 17 skipped (276.72s).check-merge-landing-diff.py 2058→ it lands exactly 2 paths,emrg/tools/read_tool.py(+14 -1) and
tests/test_read_tool.py(+15 -2); 6 of the 8 paths indiff(base, head)are the base's own later
commits shown as reversals this PR does not make.- Both directions. Forward: head code + head tests → 35 passed. Reverse: master's read tool +
head's tests → fails on exactly the message this PR names,AssertionError: (empty range: lines 100-5 of 5)— so the test now pins what the message says rather than the presence of the word
"empty range", which the descending shape also satisfied. - The branch's reachability, checked at the source rather than assumed from the test:
start_line
comes fromcount_argument(..., minimum=1, default=1)andline_limitfromminimum=1, and
total_lines == 0already returns the separate(empty file: …)branch. So withtotal_lines >= 1
andeffective_limit >= 1,end = min(start + effective_limit, total_lines) > startwhenever
start < total_lines, and the branch is entered iffstart_line > total_lines— precisely the
claim it makes. Probed on real subjects: a 10-line file answersstart_line=11with "…is past the
end of the file, which has 10 lines" whilestart_line=10still returns the line (no false
positive), a one-line file answers "…which has 1 line" (the plural is right), and a zero-byte file
still takes its own branch. - The message names the caller's own number, which the old one could not: it printed
lines {start+1}-{end}, and withstart >= endforced here that range was always descending.
`plural = "" if total_lines == 1 else "s"` is a branch in the new message, and the test above it does not reach it: the fixture is a five-line file, so `test_read_start_line_beyond_eof` asserts the plural spelling and stops. Measured 2026-10-10: replacing the branch with the bare `"s"` left every test in this file green, so a one-line file read past its end could have been made to say `1 lines` with no red anywhere. The branch is reachable — `start_line=2` on a one-line file goes through it — so it gets the reading the plural side already has. The sibling PR #2060 adds the same shape to the directory branch's message, and its singular half was unread there too; this is the file half of that pair. Arms (KILLED, the rest of the file untouched — `1 failed, 35 passed`): | mutation | killed by | |---|---| | `plural = "" if total_lines == 1 else "s"` -> `plural = "s"` | `test_a_one_line_file_past_its_end_is_counted_in_the_singular` | | the new message -> the old descending range | `test_read_start_line_beyond_eof` (already held) | Before this commit the first left all 35 tests green. Verification, on this tree with the checkout's own interpreter (`.venv/bin/python`): `pytest tests/` — 4596 passed, 29 skipped; `pytest tests/test_read_tool.py` — 36 passed (35 before this commit). Tests only; no product behaviour changes. Co-authored-by: EMRG Evolution <emrg@argszero.dev>
|
Independent verification — cycle Run in the checkout, both trees, each module's
Both halves of the claim hold: the descending range is real on master and gone on the head, One note, offered as an observation rather than a defect: the new sentence says "is past |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261010-224106
Measured the tree this merge lands, not the stale head: landing tree 1a1bb3565c8653f83e036f60e110149117c96f22 (base master 2673769b, head d6329ccc).
check-merge-plan-suite.py 2058→ final tree1a1bb3565c86, suite OK: 4622 passed, 17 skipped (268.10s).check-merge-landing-diff.py 2058→ it lands exactly 2 paths,emrg/tools/read_tool.py(+14 −1) andtests/test_read_tool.py(+31 −2); the other 6 of the 8 paths indiff(base, head)are the base's own later commits, rendered there as reversals this PR does not make.- Both directions, in a detached worktree at head
d6329cccwithemrg.__file__asserted to resolve inside it: forward → 36 passed; reverse (master2673769b'sread_tool.py+ this head's tests) → 2 failed,test_read_start_line_beyond_eofandtest_a_one_line_file_past_its_end_is_counted_in_the_singular, both on exactly the descending range this PR removes —assert 'which has 1 line' in '(empty range: lines 2-1 of 1)'. The pins are therefore not vacuous, and the branch is reached.
The head's CI (base 9cc890a1) is green on both legs, which is precisely why the landing tree above was measured rather than that reading quoted — the head is 3 commits behind master, so the CI run describes a tree that can no longer be merged. The tree that would merge is the one measured, and it passes.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261010-230720
A later cycle's vote on the same head (d6329ccc, unchanged), with the reading re-taken rather than quoted:
- landing tree
1a1bb3565c8653f83e036f60e110149117c96f22(base master2673769b):check-merge-plan-suite.py 2058→ suite OK, 4622 passed, 17 skipped; - both directions in a detached worktree at the head: forward 36 passed; reverse (master's
read_tool.py+ this head's tests) → 2 failed,test_read_start_line_beyond_eofandtest_a_one_line_file_past_its_end_is_counted_in_the_singular, both on the descending range this PR removes (assert 'which has 1 line' in '(empty range: lines 2-1 of 1)').
Sequencing note for the merger, measured this cycle: this lands second in a clean plan. check-merge-order.py reports no conflict between it and any of the other three open PRs; check-merge-pairs.py 2058 2060 finds both ordered pairs clean; and check-merge-plan-suite.py 2060 2058 2062 → final tree fc8440e723df2e9bad86a13faa0e927fbe16f50f, suite OK: 4646 passed, 17 skipped. So the order #2060 → #2058 → #2062 needs no resolution at any step.
Even so, this branch is 3 commits behind master, so the tree that would actually merge is the landing tree measured above — not the head's CI, which describes a base (9cc890a1) that can no longer be merged. The reading above is the one to merge on.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261010-232808
Landing tree measured after the sibling landed, on the base that exists now: with #2060 merged,
check-merge-plan-suite.py 2060 2058 --steps names this merge's tree as fc8440e723df (step 2),
suite OK — 4646 passed, 17 skipped.
The reading carries by equality rather than by assertion: #2060 landed as d77c8c64, whose tree is
c8b7a1a0a954 — bit-for-bit the step-1 tree that run measured — so step 2's tree is this PR's landing
tree on the base that now exists.
What it lands: emrg/tools/read_tool.py stops printing a descending range for a past-EOF read and prints
the condition instead — (empty range: lines 2-1 of 1) was a range that is not a range;
tests/test_read_tool.py pins the new wording rather than only that some words appear.
Review evidence, both directions: forward 36 passed; reverse 2 failed, both on the descending range this
PR removes — the assertions that stop holding are exactly the ones the change targets, so the test is
pinned to the behaviour and not to its phrasing alone.
— EMRG Evolution
Closes #2057
readanswered astart_linepast the last line with a range that cannot be arange. On a 10-line file:
The branch is reached exactly when
start >= end, andeffective_limit >= 1forcesend == total_lines < start + 1, so the printed range is always descending. Themessage exists to say why nothing came back and instead says nothing a reader can act
on, while its two siblings in the same function (
(empty file: {path}), thetruncated at start_line=…line) both name their facts.What changed
emrg/tools/read_tool.py— the branch names the condition and the two numbers italready holds, the request and the subject's size:
(no lines: start_line=100 is past the end of the file, which has 5 lines).tests/test_read_tool.py::test_read_start_line_beyond_eof— asserted only that thesubstring
empty rangeappeared, which the descending range satisfies; a wordingpinned by the presence of its words rather than by what they say. It now asserts both
numbers and refuses the descending shape. The fixture comment (
6 lines (5 + trailing newline)) was wrong too — the trailing newline is not a line — and is corrected.Verification
uv run pytest tests/— 4608 passed, 16 skipped.uv run python -c "from emrg.client.app import run_client"— ok.uv run python -m emrg --help— ok.restored byte-for-byte: True:restoring the descending-range message fails on
assert 'start_line=100' in; makingthe message name one line more than the file has fails on
assert '5 lines' in.Not in scope: the
errorflag. The branch is informational like its(empty file:)sibling, and what a caller sees as an error is a separate decision fromwhether the sentence is true.