Skip to content

fix(cli): preserve trailing newline in forward output - #232

Closed
LunaMeerkats wants to merge 1 commit into
QuantEcon:mainfrom
LunaMeerkats:agent/fix-forward-trailing-newline
Closed

fix(cli): preserve trailing newline in forward output#232
LunaMeerkats wants to merge 1 commit into
QuantEcon:mainfrom
LunaMeerkats:agent/fix-forward-trailing-newline

Conversation

@LunaMeerkats

@LunaMeerkats LunaMeerkats commented Jul 28, 2026

Copy link
Copy Markdown

Fixes #116.

What changed

  • Normalize forward output once before the local and --github paths split.
  • Append one LF only when the generated document has no trailing newline.
  • Preserve existing LF and CRLF endings byte-for-byte.
  • Add local-write, GitHub-mode, LF-preservation, and CRLF-preservation coverage.
  • Keep the fix under the current [Unreleased] heading after rebasing onto current main,
    retaining the entries added by PRs 246 and 247.

Reproduction

Before the fix, the focused forward.test.ts suite failed the two new local and
GitHub-path assertions (2 failed, 9 passed): both outputs returned
endsWith("\n") === false.

Validation

  • Focused regression: 13/13 passed.
  • Six affected forward suites: 69/69 passed, with one snapshot.
  • npm run lint: passed.
  • Prettier output for both committed TypeScript blobs was byte-identical when checked through
    --stdin-filepath.
  • npm run check-dev-refs: 133 references passed.
  • npm run build: passed; the committed Action bundle was byte-identical because this is a
    CLI-only path.
  • npm audit --omit=dev: one moderate undici advisory on both the branch and exact current
    main 842528b; this PR changes no dependency.
  • Broad Windows run: 1,432/1,455 tests passed. A freshly installed and built worktree at exact
    current main 842528b produced the identical 11 failing suites and 23 failing tests
    (1,430/1,453 passed). Those shared failures are pre-existing CRLF, Windows path-separator,
    and symlink-permission incompatibilities; this branch adds exactly two passing tests.

A direct final-worktree Prettier check reports the Windows core.autocrlf checkout of the two
tracked TypeScript files; the committed LF blobs pass the exact formatting oracle above.

Provenance

Authored and submitted by OpenAI Codex operating autonomously under a user-configured public
open-source contribution workflow. No human pre-submission review is claimed.

@LunaMeerkats
LunaMeerkats force-pushed the agent/fix-forward-trailing-newline branch from 34a7c50 to 4e9c137 Compare August 4, 2026 07:28
@LunaMeerkats
LunaMeerkats force-pushed the agent/fix-forward-trailing-newline branch from 4e9c137 to 4d98a34 Compare August 5, 2026 03:30
mmcky added a commit that referenced this pull request Aug 11, 2026
PR #232 (@LunaMeerkats) fixed the same defect independently and carried
explicit tests that already-terminated content — CRLF included — survives
byte-for-byte. The behavior was already guaranteed here (`endsWith('\n')`
is true for `\r\n`), but the pin is worth keeping; adopted with credit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mmcky added a commit that referenced this pull request Aug 11, 2026
…266)

* fix(forward): terminate resynced lecture files with a newline (#116)

The translator trims every model response and nothing on the forward path
put a terminator back, so each resynced file was written with its last line
unterminated — `\ No newline at end of file` in all 69 Track B PR diffs on
lecture-python.zh-cn, dirtying the next edit's diff on each file and tripping
POSIX-minded tooling.

The newline is added in `finalizeResyncContent`, which already returns the
exact bytes that get written (the structural-parity guard runs on its output),
so the local write and the --github commit both inherit it from one place
rather than each write site remembering. --test mode terminates its mock the
same way so the smoke path exercises the same bytes.

Narrow by construction: already-terminated content is byte-identical, trailing
blank lines are preserved rather than collapsed, and empty content stays empty
so it remains falsy and still skips the write.

Not fixed here: `translate init` writes `injectHeadingMap(applyTypography(…))`
of the same trimmed model output and carries the same defect — out of #116's
scope, recorded in the .dev log entry as a sibling worth filing. The action's
sync path was never affected (`reconstructDocument` has always ended with
`.trim() + '\n'`), and forward's state files come from `yaml.dump`, which
terminates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* style: prettier the new finalize tests

`format:check` is a separate CI step from `lint`; the new #116 test's
multi-line call fits on one line under the repo's print width.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(forward): adopt #232's LF/CRLF byte-for-byte preservation cases

PR #232 (@LunaMeerkats) fixed the same defect independently and carried
explicit tests that already-terminated content — CRLF included — survives
byte-for-byte. The behavior was already guaranteed here (`endsWith('\n')`
is true for `\r\n`), but the pin is worth keeping; adopted with credit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mmcky

mmcky commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Thanks so much for this contribution, @LunaMeerkats — and apologies that it sat without review for two weeks. You diagnosed #116 correctly and fixed it cleanly, and the validation notes (especially the careful Windows baselining) were genuinely helpful when comparing approaches.

The fix ended up landing via #266, which places the newline append inside finalizeResyncContent rather than at the output split — that function already owns the exact bytes that get written (the structural-parity guard runs on its output), and a planned refactor (#172) will consolidate all write-path finalization there, so the invariant travels with it. The two implementations were behaviorally identical; placement was the deciding factor.

Your LF/CRLF byte-for-byte preservation tests outlived the comparison, though: they pin a stronger contract than the property assertions #266 originally carried (any silent byte rewrite on the write path fails them, not just a missing terminator), so they were adopted into #266 with credit — see the it.each block in src/cli/__tests__/forward.test.ts and commit c12091b.

Thanks again for taking on a good first issue here — contributions like this one are very welcome, and we hope to see you around the QuantEcon repos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

forward CLI writes lecture files without a trailing newline

2 participants