Problem
Applying a delta to a spec with CRLF line endings rewrites the entire file to LF. The content change is correct, but every line of the file registers as modified, so the actual change is buried in the diff.
This is the write-side counterpart to #77. That issue fixed reading CRLF (the parsers normalize on the way in, and openspec validate is correct today). Nothing restores the convention on the way out.
On Windows, core.autocrlf=true is the Git default, so specs are CRLF on disk and this hits on every archive.
Reproduce
git init repro && cd repro
mkdir -p openspec/specs/demo openspec/changes/test/specs/demo
# a spec with CRLF endings, as a Windows checkout produces
printf '# demo Specification\r\n\r\n## Purpose\r\n\r\nDemo.\r\n\r\n## Requirements\r\n\r\n### Requirement: Existing Thing\r\nThe system SHALL do the existing thing.\r\n\r\n#### Scenario: It works\r\n- **WHEN** invoked\r\n- **THEN** it works\r\n' > openspec/specs/demo/spec.md
# a change adding one requirement
printf '## ADDED Requirements\r\n\r\n### Requirement: Another Thing\r\nThe system SHALL do another thing.\r\n\r\n#### Scenario: Another works\r\n- **WHEN** invoked\r\n- **THEN** it works\r\n' > openspec/changes/test/specs/demo/spec.md
printf '# Test\r\n\r\n## Why\r\n\r\nTesting the CRLF round-trip behavior in git diffs.\r\n\r\n## What Changes\r\n\r\n- Add another thing.\r\n' > openspec/changes/test/proposal.md
printf '## 1. Tasks\r\n\r\n- [x] 1.1 Do it\r\n' > openspec/changes/test/tasks.md
git add -A && git -c user.email=t@t -c user.name=t commit -qm base
openspec archive test --yes
git diff --numstat openspec/specs/demo/spec.md
Result
21 14 openspec/specs/demo/spec.md
The change added one requirement (~7 lines). file confirms the spec is no longer CRLF:
openspec/specs/demo/spec.md: ASCII text
Expected
7 0 openspec/specs/demo/spec.md
with the file still reported as ASCII text, with CRLF line terminators.
Notes
writeUpdatedSpec in src/core/specs-apply.ts writes the rebuilt string as-is. The rebuilt string is assembled entirely with \n because the parsers normalized on read, so whatever the file used originally is lost.
FileSystemUtils.updateFileWithMarkers has the same shape: it composes the managed block with \n and splices it into the existing content, so installing shell completions into a CRLF .bashrc or .zshrc leaves the file with mixed endings. bash reports a stray \r there as $'\r': command not found.
Worth noting why CI does not catch this: .gitattributes pins only skills/** to LF, and the test fixtures are written by the tests themselves using \n, so the windows-latest job only ever exercises LF content.
I have a fix with tests and am happy to open a PR.
Problem
Applying a delta to a spec with CRLF line endings rewrites the entire file to LF. The content change is correct, but every line of the file registers as modified, so the actual change is buried in the diff.
This is the write-side counterpart to #77. That issue fixed reading CRLF (the parsers normalize on the way in, and
openspec validateis correct today). Nothing restores the convention on the way out.On Windows,
core.autocrlf=trueis the Git default, so specs are CRLF on disk and this hits on every archive.Reproduce
Result
The change added one requirement (~7 lines).
fileconfirms the spec is no longer CRLF:Expected
with the file still reported as
ASCII text, with CRLF line terminators.Notes
writeUpdatedSpecinsrc/core/specs-apply.tswrites the rebuilt string as-is. The rebuilt string is assembled entirely with\nbecause the parsers normalized on read, so whatever the file used originally is lost.FileSystemUtils.updateFileWithMarkershas the same shape: it composes the managed block with\nand splices it into the existing content, so installing shell completions into a CRLF.bashrcor.zshrcleaves the file with mixed endings.bashreports a stray\rthere as$'\r': command not found.Worth noting why CI does not catch this:
.gitattributespins onlyskills/**to LF, and the test fixtures are written by the tests themselves using\n, so thewindows-latestjob only ever exercises LF content.I have a fix with tests and am happy to open a PR.