Repository navigation
Pick the line terminator for spliced lines through one line_endings::terminator #815
Description
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: refactor. Source: review 4.4 and 5.4 (CRLF policies); register E16. Child 1 of tracking #814.
Problem
Writers that insert or append lines pick the terminator with three hand-written rules (main @ 045d7ec):
- Any
\r\n→ CRLF, written 14 times:vendor/common.rs#L157-L163detect_eol, used by go.sum, go.mod, requirements and yarn classic;vendor/pypi_uv.rs#L1011-L1017newline_of, a byte-identical private copy;- inline
if x.contains("\r\n") { "\r\n" } else { "\n" }informats/gem/hosted.rs#L65,vendor/jvm/maven_reactor.rs#L1519,[`#L1776`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs#L1776),``upstream/composer.rs#L259-L263,[`upstream/gem.rs#L535`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs#L535),``upstream/pypi.rs#L629,[`redirect/pipenv.rs#L129`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/patch/redirect/pipenv.rs#L129), [`redirect/npmrc.rs#L614`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/patch/redirect/npmrc.rs#L614), [`redirect/mod.rs#L5775`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/patch/redirect/mod.rs#L5775), [`utils/python_script.rs#L44`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/utils/python_script.rs#L44); plus the `crlf` flags in [`upstream/cargo.rs#L304`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs#L304``) andvendor/pypi_pipenv.rs#L153.
- The first line's terminator:
vendor/jvm/gradle.rs#L855-L861, a second privatenewline_ofwith a different rule under the same name. - Majority:
utils/line_endings.rs#L74-L82majority_terminator(JsonLayout, composerlock_text).
Drift, proved by execution. A throwaway unit probe on main, run twice, gave:
- for
"a\nb\r\nc\r\n":detect_eolandpypi_uv::newline_of→ CRLF,gradle::newline_of→ LF,majority_terminator→ CRLF; - for
"a\r\nb\nc\n": the first two → CRLF,gradle::newline_of→ CRLF,majority_terminator→ LF.
On a uniform file all three agree.
Symptoms
None filed for these sites specifically. The same disagreement produced #467 and #695 in other writers (see #814).
Impact
Low risk, small. The rules differ only on mixed CRLF/LF files, where a new line comes out in a style that depends on which copy the writer was pasted from.
Proposed change
- Add
pub(crate) fn terminator(text: &str) -> &'static strtoutils/line_endings.rs:LineEndings::Crlf→"\r\n",Mixed→majority_terminator(text),Lf | None→"\n". - Replace every site listed above with it.
- Delete:
vendor::common::detect_eol,vendor::pypi_uv::newline_of,vendor::jvm::gradle::newline_of, and the 12 inline copies.yarn_classic_lock::block_eolkeeps its block-local rule but falls back toterminator(its Yarn classic hosted and vendored rewrites convert LF lines of a mixed CRLF/LF yarn.lock to CRLF, so rollback is not byte-exact #467 behavior is child 3 of Tracking: classify line endings in one place instead of five drifting rules #814). - Behavior is unchanged for LF-only and CRLF-only files. On a mixed file, inserted lines take the majority style. Majority is stable under appending lines in its own style, so revert paths that remove
{line}{nl}(uv[manifest], go.sum, Gradleappend_line) still match what the forward pass wrote.
Size and scope
About 15 files, roughly −60/+40 production lines, plus unit tests. Out of scope: preserve_line_endings and crlf_to_lf (child 2), yarn classic's mixed-file posture (child 3, #467), the pnpm CRLF refusal (review 4.7 G) and the Poetry/PDM rule (PR #703).
Acceptance criteria
-
rg 'contains\("\\r\\n"\) \{ "\\r\\n"' crates/socket-patch-core/srcfinds no production hits;detect_eoland bothnewline_offunctions are gone. - New unit tests in
utils/line_endings.rsforterminator: LF, CRLF, None, and mixed with a CRLF majority, an LF majority and a tie. - A regression test on one append-and-revert writer (for example
go_sum_editor Gradleappend_line) over a mixed file: forward then revert is byte-exact. - These stay green:
crlf_go_sum_preserves_line_endings,test_crlf_go_mod_preserves_line_endings,rewrites_hash_pinned_continuation_and_preserves_crlf,transitive_append_crlf_root_without_trailing_newline,crlf_pyproject_and_lock_round_trip_byte_exact,kotlin_settings_crlf_and_missing_newline_are_preserved,crlf_and_tabs_are_preserved(maven_reactor),gem_crlf_lock_checksum_pinned_preserving_crlf,gem_crlf_lock_checksums_header_gains_crlf_entry,gemfile_rerun_on_crlf_checkout_never_nests, and the fullcargo test -p socket-patch-core.
Dependencies
Blocked by nothing. It touches gradle.rs and maven_reactor.rs lines that #715's children also edit, so rebase whichever lands second. It unblocks children 2–4 of #814.
Activity
audit-ecosystems: file E16 (#814, #815), E38 (#816), add E60
refactor: re-rank queue at capacity (2026-10-05)
mikolalysenko commentedon Oct 5, 2026
[agent] Triaged:
priority:p3(a cross-cutting refactor). This is child 1 of tracking issue #814. Confirmed on main:common::detect_eol,pypi_uv::newline_ofandgradle::newline_of(which follows a different rule) all still exist. Not a duplicate, and no open PR covers it.Generated by Claude Code
mikolalysenko commentedon Oct 8, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: the only refactor slice left whose files no open PR changes; it collapses 8 copies of the "any
\r\n→ CRLF" rule into oneline_endings::terminator). Branch: arch-refactor/815-line-terminator. Claim-ID: 2026-10-08T03:56:03Z-4c3126Slice taken (slice 1): add
utils::line_endings::terminatorand route the copies in files no open PR touches:formats/gem/hosted.rs,redirect/upstream/{composer,gem,pypi}.rs,redirect/pipenv.rs,utils/python_script.rsandvendor/go_mod_edit.rs(2 sites). Remaining (slice 2, after the open PRs that change those files land):vendor::common::detect_eoland its callers,pypi_uv::newline_of,maven_reactor.rs×2,redirect/mod.rs, thecrlfflags inupstream/cargo.rs/npmrc.rs/pypi_pipenv.rs, and the Gradlenewline_of.Generated by Claude Code
mikolalysenko commentedon Oct 8, 2026
[agent] Draft PR for slice 1: #1108.
Generated by Claude Code
Pick inserted line endings in one place
Re-bless go and uv goldens for mixed endings
Refactor run: #1108 opens slice 1 of #815
Reuse a CRLF trailing blank in mixed go.mod
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: every other ranked slice touches files open PRs change; this one collapses
common::detect_eolandpypi_uv::newline_ofintoline_endings::terminator). Branch: arch-refactor/815-line-terminator-2. Claim-ID: 2026-10-09T04:55:52Z-b2f6b6Slice taken (slice 2): delete
vendor::common::detect_eol(callersgo_sum_edit.rs,yarn_classic_lock.rs,formats/yarn/blocks.rs,pypi_requirements.rs) andvendor::pypi_uv::newline_of, and route thenpmrc.rsinsert terminator throughterminator. Remaining after it:maven_reactor.rs×2 (#1036),redirect/mod.rs(several open PRs), and the Gradle first-line rule (gradle::eol::newline_of, paired withsniff_crlf/apply_eolfor block matching, so it moves as a unit in its own slice). The whole-filecrlfflags (upstream/cargo.rs,pypi_pipenv.rs) belong to children 2 and 4.Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR for slice 2: #1227.
Generated by Claude Code
Pick vendored line terminators in one place
refactor: run 2026-10-09T05:19Z, #1227 for #815 slice 2
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: the top-ranked slices touch files open PRs change; this one deletes the Gradle-only line-ending module and its first-line rule, plus the two
maven_reactor.rscopies, so onlyredirect/mod.rsis left). Branch: arch-refactor/815-jvm-line-terminator. Claim-ID: 2026-10-09T12:00:03Z-78dc6bSlice taken (slice 3):
gradle::eol::{newline_of, sniff_crlf, apply_eol}and thevendor/jvm/gradle.rsnewline_ofwrapper go throughline_endings::terminator, forward and revert together so a mixed file still reverts byte-exact;eol_eqand the byteto_lfmove intoutils::line_endingsandgradle/eol.rsis deleted;maven_reactor.rs×2 useterminator. Remaining after it: theredirect/mod.rsGemfile site (several open PRs change that file).Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR for slice 3: #1264.
Generated by Claude Code
Pick JVM line terminators via line_endings
mikolalysenko commentedon Oct 12, 2026
[agent] Janitor: releasing the stale claim
Claim-ID: 2026-10-09T12:00:03Z-78dc6b. slices merged, last #1264 on 2026-10-09 14:58Z; no open PR for the rest of this issue, and the claimer has been silent since 2026-10-09T12:00:39Z (more than 48h). Removingagent:claimedso another fixer can pick up what remains.Generated by Claude Code