With `--max-lines`, `-n` numbers the surviving lines 1..N (including the `[N more lines]` marker), so an agent told to edit "line 5" edits a different line; plus `--max-lines 0` emits brace soup and `-l minimal` drops the shebang
Three edge cases in rtk read, all reproduced. The first is the one that can cause a wrong edit.
*Provenance: AI-assisted source audit, human-directed, at v0.44.2 (700bdde). Reproduced with the shipped binary; input and output verbatim. Extends #3421, which covers the same function's silent splicing.*
1. -n numbers the kept lines, not the file's lines
auth.rs on disk:
1 fn can_admin(u: &User) -> bool {
2 if !u.is_admin {
3 return false;
4 }
5 if u.is_banned {
6 return false;
7 }
8 true
9 }
10 fn danger() {
11 wipe();
12 }
$ rtk read auth.rs --max-lines 8 -n
1 │ fn can_admin(u: &User) -> bool {
2 │ if !u.is_admin {
3 │ return false;
4 │ }
5 │ }
6 │ }
7 │ fn danger() {
8 │ [5 more lines]
Rows 1-4 happen to be correct. After that nothing lines up:
- Row 5 shows
}. Line 5 of the file isif u.is_banned {. - Row 7 shows
fn danger() {. That is line 10 of the file; line 7 is}. - Row 8 numbers the
[5 more lines]marker as though it were file content. There is no line 8 in this output at all.
An agent given this and asked to change line 5 edits the wrong line. src/cmds/system/read.rs:157 numbers the post-filter sequence rather than carrying each surviving line's original index.
Also visible here, and the reason #3421 exists: if u.is_banned { was dropped while its } survived, so the ban check reads as absent.
2. --max-lines 0 produces brace soup instead of nothing or an error
$ rtk rewrite head -0 auth.rs
rtk read auth.rs --max-lines 0
$ rtk read auth.rs --max-lines 0
fn can_admin(u: &User) -> bool {
}
}
}
fn danger() {
}
Six lines of orphaned braces for a request of zero lines. src/core/filter.rs:349 tests kept_lines >= max_lines - 1; with max_lines == 0 that subtraction underflows. Cargo.toml:45 sets panic = "abort" but not overflow-checks, so a release build wraps to usize::MAX and the break never fires — a debug build panics with attempt to subtract with overflow instead.
Reachable through the hook, since head -0 file rewrites to --max-lines 0.
3. -l minimal drops the shebang
$ cat s.sh
#!/usr/bin/env bash
set -euo pipefail
rm -rf "$TARGET"
$ rtk read s.sh -l minimal
set -euo pipefail
rm -rf "$TARGET"
filter.rs:202 treats #!/usr/bin/env bash as a # line comment. The output reads as a complete script but has lost the line that decides which interpreter runs it.
Suggested directions (untested — no programmer has reviewed these)
- Carry each kept line's original index through the filter and print that with
-n; never number a synthetic marker line. If the original index is not available, omitting-noutput entirely would be better than numbering that does not match the file. - Handle
max_lines == 0explicitly — return empty, or reject it — rather than relying onmax_lines - 1. - Exempt a first-line
#!from#-comment stripping.
Item 1 is the one I would prioritise: wrong line numbers are worse than no line numbers, because they look authoritative and an agent will act on them.
Source: rtk-ai/rtk