{{ message }}
Repository navigation
docs(attachment): state the real termination argument for the defuse loop - #4372
Merged
aheritier merged 1 commit intoOct 5, 2026
Merged
Conversation
Collaborator
…loop maxDefusePasses justified its bound with an argument that is measurably false: replacing a delimiter with the placeholder grows the body, because the placeholder is 42 bytes and </document-x> is 13. The claim that one pass can leave a residue is wrong too, since envelopeTagRe stops at the first '>' and so consumes a nested delimiter in the same pass. Termination actually rests on the replacement text containing no '<': a replacement can never introduce a delimiter start, while every match consumes at least one '<', so each changing pass strictly reduces the body's '<' count. Record that, since the loop returns silently when the bound is exhausted and the placeholder's character set is therefore load-bearing. Comment-only change; no behaviour change. Signed-off-by: PerryLink <255665900+PerryLink@users.noreply.github.com>
PerryLink
force-pushed
the
fix/4054-defuse-termination-comment
branch
from
September 21, 2026 15:08
386a0b5 to
50dda3c
Compare
Contributor
Author
aheritier
enabled auto-merge
October 5, 2026 11:57
aheritier
approved these changes
Oct 5, 2026
PerryLink
added a commit
to PerryLink/perrylink
that referenced
this pull request
Oct 5, 2026
A merge landed after the last round was written: docker/docker-agent#4372, merged 2026-10-05T12:14:03Z by maintainer aheritier. That is the whole of the movement - the contributor set goes 43 -> 44 repositories and 317 -> 318 merges, the merged total 350 -> 351 across 45 -> 46 repositories, open proposals 94 -> 102 across 56 -> 64, and the star table 20 -> 21 rows with Docker joining as the seventh company or jointly-governed organization. laya#943 also opened, so the laya line reads 46 merged of 50 opened, 1 open and 3 closed unmerged rather than 0 open. All twenty star counts re-measured.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fixes #4054.
maxDefusePassesjustified its bound with an argument that is measurably false, and the issue is right about that. While correcting it I found the argument wrong in a third way the issue does not mention, so this records the real invariant instead of a patched-up version of the old one.What was wrong
</document-x>is 13, so defusing grows the body. Measured:13 -> 42, and26 -> 43.envelopeTagReruns to the first>, so</TAG</TAG>>is a single match and any nested delimiter is consumed with it. One pass is always sufficient.117 -> 50 (shrank)for many delimiters. I measured</document-x>× 9 (117 bytes) as117 -> 378 (grew). Shrinking happens when a match span is longer than 42 bytes, not when there are many delimiters.What actually holds
Termination rests on one property of the replacement text:
delimiterPlaceholdercontains no<. A replacement can therefore never introduce a delimiter start, while every match consumes at least one<. Each changing pass strictly reduces the body's<count, which is a non-negative integer — so the loop cannot run forever.I verified "one pass is always enough" rather than asserting it: brute-forced 846,383 bodies (all
{<,>}strings up to length 14, plus token-alphabet enumeration to depth 5). WithmaxPasses=1, zero delimiters survived and the<count never increased.The placeholder's character set is worth recording as load-bearing rather than cosmetic: the loop returns whatever it has once the bound is exhausted, silently and with no error, so a placeholder that gained an angle bracket (say
<removed>) could let a live delimiter through inside the envelope.Also
envelope_test.go:95's comment repeated the same false premise, so it is corrected too. The assertion itself is correct and unchanged — a nested delimiter must genuinely leave no residue, whether it is neutralised in the matching pass or a later one.Scope
Comment-only, 2 files, +23/−9. Verified no code line changes: every added/removed line is a
//comment.task test(go test ./...) has unrelated failures inpkg/rag/treesitter(needsCGO_ENABLED=1) andpkg/workspacemedia(Windows symlink privilege); both reproduce with my changes stashed.