Found by the round-2 audit of #572 and sharpened by its fix pass. Pre-existing and currently inert — filed because it is a false comment on the one function every safeTerm call site in this repo funnels through, and AGENTS.md treats a comment as a claim.
The claim
internal/saferune/saferune.go (the Strip doc block) states that the output is:
a subsequence of the input, in the input's order, byte-for-byte
The measurement
Measured at a539d69 (PR #572's head) with a throwaway test inside the package:
| input |
output |
identical |
"\xff" |
"\xff" (1 B) |
yes |
"a\xffb" |
"a\xffb" (3 B) |
yes |
"\xff" (4 B) |
"�" (3 B) |
no |
"a\xffbc" (7 B) |
"a�bc" (6 B) |
no |
So the claim is false whenever the input holds both invalid UTF-8 and a stripped rune: the invalid 0xff is not dropped, it is replaced by U+FFFD. That is a byte the input never contained, which no subsequence can produce.
The sharper half — the two paths disagree with each other
This is not one wrong sentence. Strip has a fast path:
if !strings.ContainsFunc(s, Stripped) { return s }
Inputs with no stripped rune return untouched, invalid bytes and all. Inputs with one fall through to the range-over-string rewrite, where Go's UTF-8 decoding yields utf8.RuneError for each invalid byte and writes U+FFFD back out.
So whether invalid UTF-8 survives depends on whether an unrelated rune elsewhere in the string happens to be stripped. Rows 1 and 3 of the table differ only by a trailing ZWSP, and that ZWSP decides the fate of the leading 0xff.
Why it is inert today, stated so nobody over-reads this
Both behaviours are safe — neither emits a terminal-controlling rune, and U+FFFD is inert. Nothing observable is broken, and no caller is known to depend on byte-for-byte preservation. This is a correctness-of-documentation issue, not a live defect. It is worth fixing anyway because saferune is the single gate the whole safeTerm series (#393, #399, #545, #552, #564, #566, #569, #572, #573) rests on, and a reader reconciling a byte count against that sentence will reach a wrong conclusion.
Closing condition
Strip's doc block describes what the function actually does on invalid UTF-8 — including that the fast path and the rewrite path differ — or the two paths are made to agree, with the doc then describing the single behaviour. Either way a test pins the chosen behaviour on an input holding both invalid UTF-8 and a stripped rune.
Checked by: go test ./internal/saferune -count=1 exits non-zero when that behaviour is changed, naming the invalid-UTF-8 case rather than a build error.
Found by the round-2 audit of #572 and sharpened by its fix pass. Pre-existing and currently inert — filed because it is a false comment on the one function every
safeTermcall site in this repo funnels through, andAGENTS.mdtreats a comment as a claim.The claim
internal/saferune/saferune.go(theStripdoc block) states that the output is:The measurement
Measured at
a539d69(PR #572's head) with a throwaway test inside the package:"\xff""\xff"(1 B)"a\xffb""a\xffb"(3 B)"\xff"(4 B)"�"(3 B)"a\xffbc"(7 B)"a�bc"(6 B)So the claim is false whenever the input holds both invalid UTF-8 and a stripped rune: the invalid
0xffis not dropped, it is replaced by U+FFFD. That is a byte the input never contained, which no subsequence can produce.The sharper half — the two paths disagree with each other
This is not one wrong sentence.
Striphas a fast path:Inputs with no stripped rune return untouched, invalid bytes and all. Inputs with one fall through to the
range-over-string rewrite, where Go's UTF-8 decoding yieldsutf8.RuneErrorfor each invalid byte and writes U+FFFD back out.So whether invalid UTF-8 survives depends on whether an unrelated rune elsewhere in the string happens to be stripped. Rows 1 and 3 of the table differ only by a trailing ZWSP, and that ZWSP decides the fate of the leading
0xff.Why it is inert today, stated so nobody over-reads this
Both behaviours are safe — neither emits a terminal-controlling rune, and U+FFFD is inert. Nothing observable is broken, and no caller is known to depend on byte-for-byte preservation. This is a correctness-of-documentation issue, not a live defect. It is worth fixing anyway because
saferuneis the single gate the wholesafeTermseries (#393, #399, #545, #552, #564, #566, #569, #572, #573) rests on, and a reader reconciling a byte count against that sentence will reach a wrong conclusion.Closing condition
Strip's doc block describes what the function actually does on invalid UTF-8 — including that the fast path and the rewrite path differ — or the two paths are made to agree, with the doc then describing the single behaviour. Either way a test pins the chosen behaviour on an input holding both invalid UTF-8 and a stripped rune.Checked by:
go test ./internal/saferune -count=1exits non-zero when that behaviour is changed, naming the invalid-UTF-8 case rather than a build error.