Skip to content
all decision records

DR-003Accepteddecided 2026-10-05

Convert Windows line endings at the upload boundary, not in the engine

docs/decisions/DR-003-windows-line-endings.md

Context

The staff skeleton's read_one_msg stops reading at \r as well as at \n. A file with Windows line endings (\r\n) therefore produces an extra empty read after every line. In practice that means two things. Every message is followed by an empty message, and the line after ### is read as an empty dictionary line, which ends stage 4 immediately. The recorded fixture case:crlf shows the result. Stage 4 reports Emoticon total: 0, and stage 5 prints only an empty line, because with an empty dictionary even commas are dropped.

Text files saved on Windows often use \r\n. A visitor who uploads one would see the program "fail" for a reason that has nothing to do with the algorithm the site is trying to explain.

Decision

The engine stays faithful. Given \r\n it behaves exactly as the C program does, and the parity tests check this. The upload path converts each \r\n pair to \n before running, and the visualiser shows a notice saying that it did so and why. A bare \r is left alone. Text typed or pasted into the editor never contains \r, because browsers normalise line breaks in a textarea to \n.

Options considered

  1. Faithful everywhere. Uploaded Windows files would produce an empty dictionary, with no hint why.
  2. Convert silently. The output would look right, but the site would be hiding a real behaviour of the program.
  3. Convert at upload and announce it. This is what I chose.
  4. Reject files with \r\n. This is safe but unfriendly, and it treats a common file format as an error.

Why

The conversion happens at the boundary where a file becomes input, so the engine and every parity claim are unaffected. Announcing it keeps the visualiser honest: the visitor can see that the bytes run differ from the bytes uploaded, and the notice explains the C behaviour that made the conversion necessary.

What happened

Unit tests cover the conversion and the notice. The fixture case:crlf pins the C behaviour on \r\n input. The line-endings family in the seeded corpus (30 inputs, with \r\n, bare \r, mixed endings, blank lines and a missing final newline) matches the compiled program on every input, which confirms that the engine itself was not changed.

One gap remains. A visitor who wants to see the original behaviour on their own Windows file cannot do it through the upload button, because the conversion always applies there.

What I'd change

Add a "run the exact bytes" toggle to the upload notice, so the C behaviour on \r\n input is one click away for anyone who wants to see it.